架構師成長之道-C語言基礎之C語言概述
1.1 信息技術發展趨勢
目前信息技術主要經歷了互聯網、移動互聯網以及以大數據、雲計算、物聯網、人工智能、區塊鏈為代表的新興技術三個階段。
-
互聯網
互聯網從20世紀90年代逐漸興起,主要是通過網絡連接了世界各地的筆記本、台式機以及背后提供后台數據服務的服務器集群,其中絕大多數筆記本和台式機都運行着Windows,macOS系統,而服務器主要是以類Unix(CentOS,Ubuntu)占據主要市場,依靠互聯網成長起來的公司有微軟、谷歌、蘋果、亞馬遜、百度、阿里巴巴、騰訊等等。 -
移動互聯網
移動互聯網主要是通過網絡連接了世界各地的移動設備(最典型的就是手機),它們絕大多數都運行着Android,IOS系統,依靠移動互聯網公司成長起來的有美團、滴滴、小米、螞蟻金服,最近(2019/08)華為發布了鴻蒙操作系統。 -
雲計算、大數據
當然隨着用戶數據的爆發式增長,以海量數據為基礎的大數據、雲計算技術在BAT,Apple,Microsoft,Amazon,Google級別的超大型互聯網公司有廣泛的應用場景。 -
物聯網
物聯網會以手機為中樞,通過網絡連接所有智能設備,包括智能家居、汽車、電視等嵌入式設備,目前小米,華為等大廠在智能家居,電視開始布局。 -
人工智能
人工智能(AI)作為當前最火爆的技術之一,國內的BAT紛紛開始根據自身核心業務布局,阿里巴巴最早將人工智能應用在電商和物流領域,而百度開發出了對話時人工智能操作系統Dueros和自動駕駛平台Apollo,而騰訊則是在游戲領域應用人工智能。 -
區塊鏈
而區塊鏈經歷了數字貨幣、智能合約、和超級賬本三個發展階段,區塊鏈技術可以廣泛使用在金融、供應鏈、物流、公共服務領域,解決互聯網的信任問題。
目前螞蟻金服、京東已經有大量的區塊鏈應用落地。
1.2 淺談計算機系統架構
1.2.1 計算機硬件系統
現代計算機是由運算器、控制器、存儲器、輸入設備、輸出設備五大部分組成,它們各司其職,完成了數據的計算、存儲、傳輸任務。
- CPU
也被稱為中央處理器,由運算器和控制器組成,其主要作用是數據計算(從內存中獲取指令並執行后將結果返回給內存或者寫入到磁盤)和控制其他設備(聲卡顯卡,鼠標鍵盤)協同工作。目前主流的CPU架構有基於Intel的復雜指令集的X86架構(32位和64位)和手機(ARM指令集),服務器(SPARC指令集)的精簡指令集。CPU通過總線(數據總線、地址總線、控制總線)和外部進行交互。 - 內存
采用編址存儲,其主要作用是用來作為程序的工作區,程序運行時其數據和指令會被加載到內存,斷電后數據會丟失。 - 硬盤
其主要作用是永久性存儲海量數據,分為機械式硬盤和固態硬盤兩大類。 - IO設備
其主要作用是用來數據的輸入輸出,常見的輸入設備包括鼠標鍵盤,常見的輸出設備包含聲卡,顯卡,打印機等等。網卡主要是負責數據在網絡上的傳輸。
在后期程序排錯時除了考慮程序本身的錯誤之外還要考慮計算機硬件故障(例如磁盤被寫滿,網絡不通)等問題。
每個計算機組件的IO性能也各不相同,核心組件(CPU,內存,磁盤)都遵循容量越大,IO性能越差,如下圖所示,匯總了它們的IO性能。
在后期程序優化時,通常需要考慮時間復雜度和空間復雜度的問題。
1.2.2 計算機軟件系統
軟件出現的作用是實現了人和計算機更好的交互,它是由開發人員采用某種編程語言來編寫一系列的指令以及根據不同的業務邏輯產生的數據組成。
1.2.2.1 軟件分類
而軟件通常被分為系統軟件和應用軟件
-
系統軟件:
系統軟件有運行在服務器端的Unix,Linux,運行在PC桌面的macOS,Windows;運行在移動設備(手機、電視)的Android,IOS;系統軟件通常是負責管理硬件資源,同時給應用軟件提供運行平台。 -
應用軟件:
應用軟件有運行在PC桌面、手機端的淘寶、京東、微信、支付寶等等。
1.2.2.2 軟件的交互方式
不同平台的軟件有不同的交互方式:
- 服務器端:
運維開發人員基於命令行的字符界面實現人機交互。 - PC桌面端:
開發人員、設計人員、產品經理、普通用戶通常是基於圖形化界面實現人機交互。 - 移動端:
普通用戶通常是通過語音,手勢觸控實現人機交互。
1.2.3 常用應用的軟硬件協作實現
計算機程序運行流程如下圖所示

- 聊天:應用程序監聽鍵盤輸入的數據,放到內存,然后傳給網卡,通過網絡傳遞給另外一個網卡,再從網卡傳到內存,顯示在顯示器上。
- 聽音樂:應用程序將音樂數據加載到內存之后,然后寫到聲卡上。
- 看視頻:應用程序將視頻數據加載到內存,然后寫到顯卡上。
- 讀文檔: 應用程序將磁盤中的文檔數據加載到內存后顯示到屏幕上。
- 寫文檔: 應用程序將內存中的數據寫入到磁盤上。
1.3 程序和指令
- 程序:
程序是為了完成某項特定任務(例如聊天,聽音樂等等)而使用某種編程語言編寫的一組指令序列。 - 指令:
指令是對計算機進行程序控制的最小單位,由操作碼和操作數組成,操作碼指的是機器要執行什么操作(例如加減乘除),而操作數就是具體執行的對象(具體的數據以及存放數據的位置),所有指令的集合稱為計算機的指令系統,常見的電腦指令系統有Intel X86指令集,常見的手機指令系統有ARM。因此手機上的應用程序不加修改是不能在電腦上直接運行,因為不同的指令集是不兼容的。
1.4 編程語言發展史及其應用場景
1.4.1 編譯器與高級語言
首先明確一點,計算機最底層只能識別二進制(010101)的機器指令,那些由匯編語言或者是高級程序設計語言編寫的應用程序只是為了方便開發人員理解和維護,這就需要將匯編語言和高級語言翻譯成計算機能夠理解的機器語言,而編譯器或者解釋器就是做這個工作的。它的出現讓開發人員在編寫程序時不用考慮底層硬件的差異性,只需要專注具體業務邏輯的實現即可。
C語言的編譯器在各個操作系統之上都有實現,其中Windows系統可以使用Visual C編譯器,而Linux可以使用GCC編譯器,macOS可以使用LLVM CLang編譯器。
1.4.2 編程語言發展史
任何事物都是從無到有,逐步發展壯大起來的,編程語言也是一樣。
計算機程序設計語言經歷了機器語言到匯編語言和高級程序設計語言三個階段,其特點是使得程序員用編程語言開發、測試、部署應用程序越來越方便、高效。但是是以犧牲效率為代價,但是隨着計算機硬件的發展,絕大多數應用場景的效率損失可以忽略不計。
- 機器語言
計算機發展的最早期,程序員編寫程序采用二進制的指令(010010101)來實現的,而每種CPU都有各自不同的指令系統(Ultra SPARC/Intel X86/ARM),因此在不同的機器上使用不同的機器語言實現。其特點是性能特別高效,而面向機器編程也就意味着不能移植,需要手動處理底層硬件的差異性,而且二進制的指令難以理解和維護。 - 匯編語言
隨着時代和計算機技術的發展,匯編語言和編譯器的出現解決了機器語言需要記住非常多的二進制機器指令的難題,但是還是沒有從根本上解決移植性的問題,只是將機器指令轉換為易懂的英文單詞,然后由編譯器編譯成機器指令,因為計算機終歸揭底只能識別二進制的0001110100110機器指令,而且匯編語言是面向機器的,不同機器(SPARC/Intel X86/ARM)的匯編指令是不相同的。 - 高級程序設計語言
高級程序設計語言的高級之處體現在開發人員在編寫程序時無需關心計算機底層硬件差異而只需要專注於業務模塊實現即可。甚至是可以實現 一次編譯,到處運行,這里以Java為例子:通過實現不同平台的JVM,編譯生成的字節碼文件可以在任意的JVM上運行。
高級語言通常都需要編譯器或者是解釋器將源碼編譯或者解釋后執行。
高級語言主要分為面向過程和面向對象兩種,其中典型的面向過程編程語言就是C,面向對象的編程語言有Java,C++等等。
1.4.3 編程語言應用場景
如果想知道目前主流的編程語言有哪些,可以訪問tiobe 首頁獲取編程語言排行榜。
2019年8月編程語言排行榜Top20

根據Tiobe排行榜得知,Java/C/Python分別排在前三名, 隨后緊跟着C++,C#等等.
每種語言都有不同的應用場景和擅長的領域。
| 編程語言 | 應用場景 |
|---|---|
| C | 硬件驅動、操作系統、系統軟件 |
| C++ | 系統軟件、網絡通訊、科學計算 、游戲 |
| C# | Windows應用,Web應用、游戲 |
| Java | 大型互聯網應用(淘寶、天貓),Android,大數據 |
| Python | 人工智能、機器學習、自動化運維、數據分析 、圖形處理 |
| PHP | 中小型網站開發 |
| Objective-C/Swift | macOS,iPhone,iPad應用開發 |
| JavaScript | 瀏覽器端、服務端、PC桌面 |
| Go | 高並發、區塊鏈 |
那么如果你作為初學者,面對如此之多的編程語言,到底應該先從哪門語言上車呢?如果你想深入的學習其他語言和架構相關的知識之前建議熟練掌握C語言。因為C++/Java/C#等編程語言都模仿了C語言。無論哪種語言,都是實現目標的工具,而不是目標本身。
1.5 C語言概覽
C語言憑借其高效率、良好的移植性、功能強大的特性在操作系統、硬件驅動以及系統應用開發占據廣闊的市場。
1.5.1 C語言發展簡史
- 起源
1972年,貝爾實驗室的Dennis Ritchie和Ken Thompson在開發Unix操作系統時設計了C語言,該操作系統的90%以上的代碼都是由C語言實現,后期的Linux,MacOS,Android,IOS都是基於Unix發展而來的。 - 標准
1987年Dennis Ritchie和Brian Kernighan編寫了The C Programming Language第一版是公認的C標准實現,而沒有定義C庫。
而后期ANSI/ISO先后於1990年、1999年和2011年發布了C90標准、C99標准和C11標准,該標准定義了C語言和C標准庫。
1.5.2 C語言特性
C語言作為面向過程的高級程序設計語言,能夠輕松的實現自頂向下的規划、結構化編程和模塊化設計,這樣使得程序結構更加簡潔,可擴展性強以及更加容易維護。
而且C語言有着高效(執行速度快)、功能強大(嵌套匯編)以及可移植性(標准庫可移植)、功能強大等優點,而且也存在着對系統平台庫依賴嚴重,由於編程風格自由,經驗不足也會容易導致出錯,編寫代碼實現周期長,同樣的代碼在不同的操作系統(或者編譯器)下可能會有不同的表現等缺點。
1.5.3 C語言應用場景
C語言偏向操作系統、硬件驅動、底層應用(數據庫、編譯器)、嵌入式應用開發、游戲引擎等應用場景。
- 硬件驅動的絕大部分實現是由C語言和匯編語言實現的。
- 主流操作系統(Unix,Linxu,MacOS,Windows,Android,iOS)的底層實現都是由C語言和部分匯編實現的。
- C++,Java,Python,Swift的編譯器或者解釋器都是由C語言實現的。
- Git,Nginx,Redis,MySQL都是使用C語言實現的,而且都是開放源代碼的,可以在GitHub中獲取到,可以通過閱讀源碼提升自己的設計和編碼能力。
1.6 C語言集成開發環境搭建
目前主流操作系統(Windows,Linux,MacOS)都有完善的C語言集成開發環境,用於編輯、編譯、調試、打包部署C程序。
| 操作系統 | 開發工具 |
|---|---|
| Windows10 | Visual Studio2019 |
| macOS10.14 | XCode10.3 |
| Ubuntu18.04 | QT5.13 |
Windows作為世界上最流行的桌面操作系統,最新版本為Windows10 1903,VisualStudio作為Windows上最強大的集成開發環境,可以開發Windows軟件,游戲,Web應用等等,最新版本為VisualStudio2019 16.2.2。

Ubuntu作為最受歡迎的桌面版Linux系統之一,推薦采用跨平台的集成開發環境QT來編寫C/C++程序。

MacOS平台推薦使用XCode來編寫C/C++程序,而且XCode在macOS系統上是自帶的,無需額外安裝。

除此以外還有些跨平台的C/C++ 開發工具,例如來自Jetbrains公司的CLion也可以用來編寫C/C++程序。

關於IDE的安裝以及使用,它們的官網都提供了詳細的教程,同學們可以自行去查閱。
| IDE | 官網地址 |
|---|---|
| Visual Studio | 下載 文檔 |
| CLion | 下載 文檔 |
| QT | 下載 開發者 |
| XCode | 官網 |
C語言學習過程中會使用Windows10 1903+Visual Studio 2019作為主力開發工具,如果沒有特別的說明,默認的環境就是Windows10 1903和Visual Studio 2019。
1.7 Visual Studio Code實現HelloWorld
HelloWorld是學習一門編程語言的入門程序,如果你能獨立編寫並運行HelloWorld,那么就意味着邁入了編程的大門了。
Visual Studio Code是微軟推出的一款開源、跨平台編輯器,這里使用它來編寫C語言的源代碼,如果想了解Visual Studio Code更多內容,可以去官網拜讀文檔。
為什么首先要使用Visual Studio Code來編寫第一個C程序呢,因為Visual Studio Code只是一個編輯器,用於編寫源碼,並不提供編譯或者解釋運行程序的功能。通過它來對比IDE,體驗下使用IDE開發程序帶來的好處,也有利於理解IDE背后的機制。
- 在D盤的code目錄下創建源文件helloworld.c,C語言的源文件都是以.c結尾的。
- 編輯源文件
#include <stdio.h>
int main(int argc, char* argv[]) {
printf("Hello World Windows10 1903 & Visual Studio Code & C ");
return 0;
}
- 編譯源文件
將源文件編譯成對應平台的可執行程序,因為計算機不能識別開發人員編寫的源文件內容,只能識別二進制的機器指令。而C語言編寫的源程序可以使用多種編譯器來編譯:Windows系統可以安裝QT或者Visual Studio 2019后編譯源文件
QT中自帶了GCC編譯器,以QT5.13為例,將QT的默認安裝路徑(C:\Qt\Qt5.13.0\Tools\mingw730_64\bin) 添加到系統的PATH環境目錄下
然后在Windows命令行的終端中輸入gcc -v,看到類似如下輸出,就可以使用gcc編譯器了
C:\Users\ittim>gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=C:/Qt/Qt5.13.0/Tools/mingw730_64/bin/../libexec/gcc/x86_64-w64-mingw32/7.3.0/lto-wrapper.exe
Target: x86_64-w64-mingw32
Configured with: ../../../src/gcc-7.3.0/configure --host=x86_64-w64-mingw32 --build=x86_64-w64-mingw32 --target=x86_64-w64-mingw32 --prefix=/mingw64 --with-sysroot=/c/mingw730/x86_64-730-posix-seh-rt_v5-rev0/mingw64 --enable-shared --enable-static --disable-multilib --enable-languages=c,c++,fortran,lto --enable-libstdcxx-time=yes --enable-threads=posix --enable-libgomp --enable-libatomic --enable-lto --enable-graphite --enable-checking=release --enable-fully-dynamic-string --enable-version-specific-runtime-libs --enable-libstdcxx-filesystem-ts=yes --disable-libstdcxx-pch --disable-libstdcxx-debug --enable-bootstrap --disable-rpath --disable-win32-registry --disable-nls --disable-werror --disable-symvers --with-gnu-as --with-gnu-ld --with-arch=nocona --with-tune=core2 --with-libiconv --with-system-zlib --with-gmp=/c/mingw730/prerequisites/x86_64-w64-mingw32-static --with-mpfr=/c/mingw730/prerequisites/x86_64-w64-mingw32-static --with-mpc=/c/mingw730/prerequisites/x86_64-w64-mingw32-static --with-isl=/c/mingw730/prerequisites/x86_64-w64-mingw32-static --with-pkgversion='x86_64-posix-seh-rev0, Built by MinGW-W64 project' --with-bugurl=https://sourceforge.net/projects/mingw-w64 CFLAGS='-O2 -pipe -fno-ident -I/c/mingw730/x86_64-730-posix-seh-rt_v5-rev0/mingw64/opt/include -I/c/mingw730/prerequisites/x86_64-zlib-static/include -I/c/mingw730/prerequisites/x86_64-w64-mingw32-static/include' CXXFLAGS='-O2 -pipe -fno-ident -I/c/mingw730/x86_64-730-posix-seh-rt_v5-rev0/mingw64/opt/include -I/c/mingw730/prerequisites/x86_64-zlib-static/include -I/c/mingw730/prerequisites/x86_64-w64-mingw32-static/include' CPPFLAGS=' -I/c/mingw730/x86_64-730-posix-seh-rt_v5-rev0/mingw64/opt/include -I/c/mingw730/prerequisites/x86_64-zlib-static/include -I/c/mingw730/prerequisites/x86_64-w64-mingw32-static/include' LDFLAGS='-pipe -fno-ident -L/c/mingw730/x86_64-730-posix-seh-rt_v5-rev0/mingw64/opt/lib -L/c/mingw730/prerequisites/x86_64-zlib-static/lib -L/c/mingw730/prerequisites/x86_64-w64-mingw32-static/lib '
Thread model: posix
gcc version 7.3.0 (x86_64-posix-seh-rev0, Built by MinGW-W64 project)
gcc編譯源文件並運行生成的可執行文件
D:\code>dir
驅動器 D 中的卷是 DATA
卷的序列號是 B0FB-1B0A
D:\code 的目錄
2019/08/18 10:48 <DIR> .
2019/08/18 10:48 <DIR> ..
2019/08/18 10:46 159 helloworld.c
1 個文件 159 字節
2 個目錄 205,122,088,960 可用字節
D:\code>gcc helloworld.c -o helloworld.exe
D:\code>helloworld.exe
Hello World Windows10 1903 & Visual Studio Code & C
Visual Stuido 2019只需要在路徑C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Visual Studio 2019\Visual Studio Tools下運行 Developer Command Prompt for VS2019后 使用cl命令編譯源文件命令即可。
**********************************************************************
** Visual Studio 2019 Developer Command Prompt v16.2.2
** Copyright (c) 2019 Microsoft Corporation
**********************************************************************
C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise>d:
D:\>cd code
D:\code>cl helloworld.c
用於 x86 的 Microsoft (R) C/C++ 優化編譯器 19.22.27905 版
版權所有(C) Microsoft Corporation。保留所有權利。
helloworld.c
Microsoft (R) Incremental Linker Version 14.22.27905.0
Copyright (C) Microsoft Corporation. All rights reserved.
/out:helloworld.exe
helloworld.obj
D:\code>helloworld.exe
Hello World macOS & Visual Studio Code & C
1.8 Visual Studio 2019集成GitHub
在正式使用Visual Studio 2019開發C語言的程序時,可以集成GitHub插件,編譯后期代碼統一管理。
Github是開源的項目托管平台,在使用它之前得先創建一個賬號。后續所有的C程序的源文件都會上傳到GitHub,這樣即使更換電腦,只需要從遠程的GitHub將項目克隆到本地即可。
-
啟動Visual Studio 2019

-
下載Visual Studio 的GitHub插件
點擊Visual Studio菜單 擴展->管理擴展

然后點擊聯機,選擇GitHub Extension for Visual Studio 點擊下載即可。

-
安裝GitHub插件
在Visual Studio 2019中下載完GitHub插件以后需要手動關閉Visual Studio 2019,然后會自動開啟安裝進程
用戶賬戶控制

此時Visual Studio會去聯網下載GitHub插件,如果遇到網絡原因下載超慢,可以掃描下方的二維碼下載

然后關閉VisualStudio2019,進入C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE目錄下

輸入cmd

此時就已經進入C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE目錄下,只需要輸入VSIXInstaller.exe C:\Users\ittim\Desktop\GitHub.VisualStudio.vsix並回車后便可開始安裝GitHub插件

開始安裝

安裝完成

- 連接GitHub
GitHub插件安裝完成以后,重啟Visual Studio 2019

首先將項目由解決方案資源管理器切換到團隊資源管理器,然后點擊連接GitHub

-
登錄GitHub
輸入用戶名和密碼登錄GitHub

-
創建倉庫
首先點擊創建菜單

然后設置倉庫名稱、描述以及本地倉庫路徑、Git忽略、License

如果一切順利,可以使用瀏覽器訪問地址https://github.com/ittimeline/c-core-programming 來訪問項目,各位同學只需要將ittimeline替換成你們的賬戶即可。

1.9 Visual Studio 2019實現HelloWorld
-
新建解決方案
在之前創建的Git倉庫c-core-programming基礎上新建項目

然后選擇項目模板:基於C++的空項目

然后設置項目名稱、路徑以及解決方案名稱

-
新建篩選器
在解決方案資源管理器視圖下,選中源文件,然后新建篩選器

篩選器的名稱為Chapter1

-
新建項

新建源文件helloworld.c

-
編輯源文件
#include <stdio.h>
#include<stdlib.h>
int main(int argc, char* argv[]) {
printf("Windows10 1903 & Visual Studio 2019 Hello World \n");
system("pause");
return 0;
}
- 運行helloworld
點擊本地Windows調試器,可以運行程序,也可以通過快捷鍵F5啟動本地Windows調試器來運行程序

程序運行結果

如果想要關閉程序,可以通過快捷鍵Shift+F5實現。
如果運行程序時出現如下提示

可以根據提示設置在調試時停止自動關閉控制台

Visual Studio遇到printf()語句自動彈出終端輸出Windows10 1903 & Visual Studio 2019 Hello World,遇到return語句關閉終端,打開終端和關閉終端時間很短,感覺程序有閃退現象。
這里可以調用stdlib.h頭文件中的system函數並傳遞字符串參數pause讓程序暫停。
- 推送代碼到遠程倉庫
首先切換到團隊資源管理器視圖下,然后點擊更改

然后填寫提交信息,點擊全部提交,此時會將代碼提交到本地倉庫

然后點擊同步到遠程倉庫

推送到遠程倉庫

然后就可以在GitHub倉庫中查看提交的內容

1.10 HelloWorld詳解
1.10.1 #include預處理
在C語言中,如果想要調用系統的某個函數,必須首先包含該函數聲明所在的頭文件,例如hellowrld程序中調用printf()函數聲明,就是位於系統目錄C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\ucrt下的頭文件stdio.h。

如果包含的文件在當前目錄下,使用#include "頭文件",如果該頭文件在系統目錄下,則使用#include <>。而編譯器查找當前目錄下的頭文件時如果未找到,還會去系統目錄下查找對應的頭文件。
#include的本質就是將包含的頭文件內容全部復制到源文件中去。
如何驗證這一點呢?
首先在項目c-core-programming-foundational的頭文件下新建篩選器Chapter1,並創建頭文件guanglei.h

然后把之前源文件helloworld.c的源碼復制到guanglei.h中

之后定義源文件guanglei.c

該源文件的內容只需要使用#include "guanglei.h" 即可運行。

但是Visual Studio 2019 解決方案(例如c-core-programming)下每個項目(c-core-programming-foundational)只能有一個包含main方法的源程序,否則程序運行時會出錯。

這里將之前helloworld程序中的main方法改成別的名字(例如helloworld_main)即可。
然后再次運行程序,運行結果和之前的helloworld程序結果一樣

#include <stdio.h>表示包含頭文件stdio.h,std表示標准庫,是可以移植的。也就是helloworld.c可以運行在各個操作系統下。
Windows10 1903 &CLion 2019.2

Ubuntu18.04 & CLion 2019.2
運行之前必須先安裝CMake,使用sudo apt install cmake安裝即可

而stdio表示標准輸入輸出,printf()函數正是一個輸出函數。
stdlib表示標准庫,system()函數正是一個標准庫函數。
頭文件只存放了函數的定義,至於函數的實現,在對應的系統庫文件中。
1.10.2 main函數
main函數是C程序的入口,一個項目只能有一個main函數,否則程序運行時會提示定義多個main函數。
當程序運行時,系統自動調用main函數,程序從main函數的第一行開始執行,如果其他函數想要得到執行,那么必須被main函數直接或者間接調用
int helloworld_main(int argc, char* argv[]) {
printf("Windows10 1903 & Visual Studio 2019 Hello World \n");
system("pause");
return 0;
}
-
main函數左邊的int是函數的返回值,右邊()的內容表示函數的參數,外部數據通過函數的參數傳遞到函數的內部處理。
-
{}表示函數的函數體,也就是函數執行的所有內容都包含在{}中,它是成對出現的。
-
printf函數是系統自帶的函數,用於將""包含的字符串按照指定的格式輸出到終端,終端可以是控制台、網頁或者是文件,目前是輸出到控制台。
-
\n表示換行,這里就是輸出內容之后換行。
-
分號(;)表示一條語句結束,C語言每條執行語句都需要以分號(;)結束,如果不加上分號會發生編譯錯誤。
-
system("pause")表示程序暫停,輸入任意鍵后退出,如果在Visual Studio中編寫C程序必須加上system("pause"),否則程序運行完成后就直接退出。使用CLion、QT不需要加上system("pause")。
-
return 0表示將int類型的值返回給main函數的調用方(系統)並結束main函數,return語句之后的代碼都不會再執行了,0表示正常結束。
在源文件function.c中定義了一個add方法,傳遞兩個整數參數,返回兩個整數相加的結果。並且該方法在主函數的prinf方法中被調用,用於輸出兩個整數相加的結果。
printf函數中的%d表示對應位置的參數按照整數輸出。
#include <stdlib.h>
int add(int source,int target) {
return source + target;
printf("不可達代碼\n");
}
int main(int argc,char* argv[]) {
int source = 50;
int target = 100;
printf("%d加上%d的結果是%d\n",source,target,add(source,target));
system("pause");
return 0;
}
程序運行結果表明add方法的return語句之后的輸出語句printf("不可達代碼\n")並沒有執行,如果想要add方法得到執行,在main方法中調用即可,調用時需要傳遞對應的參數。

1.10.3 注釋
注釋是用於描述程序的功能,為了便於程序后期的維護,在開發過程中應該添加適量的注釋,編譯器在預編譯時會刪除注釋,所以注釋的內容不會影響程序的結果。
C語言中支持的注釋包含單行注釋(//)和多行注釋(/**/)兩種,單行注釋顧名思義就是注釋一行,多行注釋可以注釋多行,需要注意的是多行注釋不能夠嵌套使用,否則會發生編譯錯誤。
增加單行注釋和多行注釋后的helloworld
#include <stdio.h>
#include<stdlib.h>
/* 我的第一個C程序 @author liuguanglei ittmelinedotnet@gmail.com @version 2019/08/20 */
int main(int argc, char* argv[]) {
//快捷鍵F12可以查看函數定義 Ctrl+ -返回
printf("Windows10 1903 & Visual Studio 2019 Hello World \n");
system("pause");
return 0;
}
1.11 Windows常用命令
1.11.1 Windows命令概述
主流的操作系統(Windows,Linux(Ubuntu),MacOS)及大多數應用程序都提供了基於命令行和圖形化界面兩種交互方式,而移動端是采用手勢觸控、語音等進行交互。作為普通用戶來講,圖形化界面容易上手,而且交互效果更加直觀。
但是作為一個程序員來講,應該去熟悉各個操作系統的命令行的使用,因為命令行相對圖形化界面而言,絕大多數場景下使用更加高效。而且圖形化界面本質是對命令行的封裝,能用圖形化界面實現的基本都可以采用命令行實現。而且在服務器端(CentOS,RedHat)基本上是不會安裝圖形界面。
Windows系統可以使用快捷鍵Windows+r調出運行

后輸入cmd,便可以進入Windows下的命令行終端

Windows下常用的文件目錄和系統應用相關的命令如下所示,只要開啟了終端就可以在終端內通過使用文件目錄相關和系統應用相關的命令實現快捷操作。
1.11.2 Windows常用命令
- 文件目錄相關命令
| 命令名稱 | 功能描述 |
|---|---|
| dir | 列出當前目錄列表 |
| cd | 切換目錄(只能在根盤符(例如C盤)內切換) |
| md | 創建目錄 |
| del | 刪除文件 |
| type | 顯示文本文件內容 |
| cls | 清除屏幕內容 |
| exit | 退出終端 |
- 系統應用相關命令
| 命令名稱 | 功能描述 |
|---|---|
| notepad | 記事本 |
| calc | 計算器 |
| mspaint | 畫圖 |
| explore | 文件資源管理器 |
| timedate.cpl | 日期和時間 |
| cleanmgr | 磁盤清理 |
| desk.cpl | 分辨率設置 |
| powercfg.cpl | 電源設置 |
| regedit | 注冊表編輯器 |
| msconfig | 系統配置 |
| mstsc | 遠程連接 |
| firewall.cpl | 防火牆 |
| appwiz.cpl | 添加或修改程序 |
| tasklist | 查看進程列表 |
| taskkill /f /im process-name.exe | 關閉指定進程 |
| msinfo | 系統信息 |
| sticky notes | 便簽 |
| ipconfig | 查看ip |
| winver | 查看windows版本 |
| echo | 顯示文本內容 例如echo %path% 查看系統環境變量 |
| ping | 檢測網絡是否暢通 |
1.11.3 system函數
system函數用於C程序中調用各大操作系統的命令和應用程序,被調用的命令和應用程序以字符串的方式作為system函數的參數傳入。
實現讀取用戶輸入的命令,並執行該命令
#include <stdio.h>
#include <stdlib.h>
/* 使用system函數執行用戶輸入的命令 @author tony ittimelinedotnet@gmail.com @version 2019/08/20 */
int main(int argc,char* argv[]) {
printf("請輸入你要執行的命令\n");
//定義數組保存用戶輸入的指令
char str[100] = { 0 };
//使用scanf_s函數讀取用戶輸入的指令
scanf_s("%s", str);
//執行命令
system(str);
return 0;
}
因為system函數位於stdlib.h頭文件中,意味着它是一個可以跨平台使用的函數,Visual Studio 2019中可以使用F12快捷鍵跳轉到函數的定義。使用Ctrl+-快捷鍵實現后退。
但是各大操作系統的命令和應用程序是不相同的,例如在Windows上使用system函數傳遞字符串參數"notepad"時可以打開記事本,而移植到了類Unix上則無法調用記事本。
1.12 C語言的運行流程
C語言編寫的程序一般都要經過編寫源文件->預處理->編譯->匯編->鏈接后運行這幾個流程。

- 預處理
預處理主要是宏替換、包含頭文件、條件編譯以及刪除注釋等操作,預編譯階段生成文件的后綴名通常是.i。 - 編譯
編譯主要是將預處理好的文件生成匯編文件,編譯后文件的后綴名通常是.s。 - 匯編
匯編主要是將匯編文件生成二進制文件,匯編后的文件的后綴名通常是.o。 - 鏈接
鏈接主要是將各個二進制文件、庫函數、啟動代碼生成可執行文件。
接下來編寫源程序program_running_process.c,然后結合Windows上的GCC編譯器提供的各個選項實現預處理、編譯、匯編、鏈接,關於GCC的安裝和配置可以參考
Visual Studio Code實現Hello World章節的內容。
#include <stdio.h>
//定義浮點型的常量
#define PI 3.14
/* C語言程序的運行流程 @author tony ittimelinedotnet@gmail.com @version 2019/08/20 */
int main(int argc, char* argv[]) {
printf("PI =%lf\n", PI);
return 0;
}
預處理使用 gcc -E 選項即可實現,其中-o表示輸出文件的名稱
D:\workspace\c\visualstuido2019\c-core-programming\c-core-programming-foundational\c-core-programming-foundational>gcc -E program_runing_process.c -o program_runing_process.i
預處理完成以后,生成的文件program_running_process.i的大小是54kb

當預處理完成以后,定義的常量被替換成實際的值,而#include包含的頭文件內容也被復制到預處理的生成的文件中

編譯通過gcc -S 選項實現
D:\workspace\c\visualstuido2019\c-core-programming\c-core-programming-foundational\c-core-programming-foundational>gcc -S program_runing_process.i -o program_runing_process.s
編譯會生成匯編文件
.file "program_runing_process.c"
.text
.def __main; .scl 2; .type 32; .endef
.section .rdata,"dr"
.LC1:
.ascii "PI =%lf\12\0"
.text
.globl main
.def main; .scl 2; .type 32; .endef
.seh_proc main
main:
pushq %rbp
.seh_pushreg %rbp
movq %rsp, %rbp
.seh_setframe %rbp, 0
subq $32, %rsp
.seh_stackalloc 32
.seh_endprologue
movl %ecx, 16(%rbp)
movq %rdx, 24(%rbp)
call __main
movq .LC0(%rip), %rax
movq %rax, %rdx
movq %rdx, %xmm1
movq %rax, %rdx
leaq .LC1(%rip), %rcx
call printf
movl $0, %eax
addq $32, %rsp
popq %rbp
ret
.seh_endproc
.section .rdata,"dr"
.align 8
.LC0:
.long 1374389535
.long 1074339512
.ident "GCC: (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 7.3.0"
.def printf; .scl 2; .type 32; .endef
匯編通過gcc -c 選項實現
D:\workspace\c\visualstuido2019\c-core-programming\c-core-programming-foundational\c-core-programming-foundational>gcc -c program_runing_process.s -o program_runing_process.o
鏈接gcc沒有提供選項,這里只要執行gcc program_running_process.o -o program_running_process.exe就會鏈接生成可執行文件
D:\workspace\c\visualstuido2019\c-core-programming\c-core-programming-foundational\c-core-programming-foundational>gcc program_runing_process.o -o program_runing_process.exe
windows下的可執行文件后綴名默認是.exe,當然gcc編譯器不關心后綴名,如果你的ubuntu或者macOS下安裝了gcc,也可以將C程序編譯生成.exe后綴的可執行文件。
在Linux下如果想要查看你的C序(編譯生成的可執行文件引用了哪些系統庫,可以使用ldd命令查看
guanglei@ubuntu:~/CLionProjects/c-core-programming$ cd cmake-build-debug/
guanglei@ubuntu:~/CLionProjects/c-core-programming/cmake-build-debug$ ls
c_core_programming c_core_programming.cbp CMakeCache.txt CMakeFiles cmake_install.cmake Makefile
guanglei@ubuntu:~/CLionProjects/c-core-programming/cmake-build-debug$ ldd c_core_programming
linux-vdso.so.1 (0x00007ffdc1fcf000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f0011a14000)
/lib64/ld-linux-x86-64.so.2 (0x00007f0012007000)
1.13 Visual Studio 2019調試程序
在后期編寫程序時,經驗不足的開發人員通常會遇到兩種錯誤:編譯錯誤和運行時錯誤,編譯錯誤通常是編寫的程序不滿足編譯器的語法規范,而運行時錯誤則是程序運行時發生的錯誤,想要排查程序運行時的錯誤,就需要使用IDE提供的調試功能。
在源文件debug.c中定義一個返回兩個整數相乘的方法mul,然后在main函數中定義兩個變量,並調用兩個整數相乘的方法,輸出計算結果
#include <stdio.h>
#include <stdlib.h>
/* 定義兩個整數相乘並返回 */
int mul(int source,int target) {
return source * target;
}
/* 調試程序 @author liuguanglei ittimelinedotnet@gmail.com @version 2019/08/21 */
int main(int argc, char* argv[]) {
//定義兩個變量
int source = 12;
int target = 12;
printf("source = %d \t target = %d \n", source,target);
//調用兩個整數相乘的方法,並將計算的結果保存到整數變量result中
int result = mul(source,target);
//輸出兩個整數相乘的結果
printf("result = %d \n",result);
system("pause");
return 0;
}
在調試程序前首先需要下斷點,可以使用快捷鍵F9下斷點或者取消斷點

然后使用F5調試運行程序,程序會在之前下過斷點的地方暫停,此時可以在Visual Studio提供的窗口中觀察變量的值

如果想要一行一行調試代碼,可以使用快捷鍵F11,當遇到函數時(例如這里的mul函數),會進入函數的內部一行一行執行,直到該函數結束為止。
如果調試時不想進入函數的內部,可以使用快捷鍵F10,當程序中調用了其他函數時不會進入其他函數內部,但是函數會被調用。
如果調試時想要從一個斷點跳到另外一個斷點,可以使用shift+F11實現。
如果想要查看函數調用的定義,可以使用快捷鍵F12,退出查看函數定義則使用Ctrl+ -。
在調試代碼的過程中,還可能會注釋部分錯誤的代碼,注釋可以使用快捷鍵Ctrl+k,u實現,而取消注釋可以使用Ctrl+k,u實現。
如果源文件中的代碼格式比較凌亂,可以使用Ctrl+k,f快速格式化代碼。
1.14 Visual Stuido 2019技巧錦集
1.14.1 Visual Studio 2019解決4096錯誤
Visual Studio 希望用戶使用Visual Studio提供的非標准庫函數,但是程序員希望使用標准庫函數,因為標准庫是可以移植的。
如果在Visual Studio中使用標准的庫函數,就會引發4996錯誤類似於這種提示:error C4996: 'strcpy': This function or variable may be unsafe. Consider using strcpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.這里的strcpy是標准的庫函數,而在Visual Studio中使用這個函數引發了4996錯誤。
#define _CRT_SECURE_NO_WARNINGS //防止4996錯誤
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* vs4996錯誤 error C4996: 'strcpy': This function or variable may be unsafe. Consider using strcpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details. @author liuguanglei ittimelinedotnet@gmail.com @version 2019/08/21 */
int main(int argc,char *argv[]) {
//定義一個字符串
char buf[32] = "";
//使用strcpy實現將字符串hi string復制到變量buf中
strcpy(buf, "hi string");
//輸出字符串的內容
printf("buf = %s \n",buf);
system("pause");
return 0;
}
如何解決Visual Studio中的4996錯誤呢?
方案1:在源文件(例如這里的vs4996.c)的第一行定義宏
#define _CRT_SECURE_NO_WARNINGS //防止4996錯誤
方案2:在源文件(例如這里的vs4996.c)的第一行定義如下內容:
#pragma warning(disable:4996)
1.14.2 Visual Studio 2019配置代碼片段
在后期編寫C程序中,需要反復使用到如下的代碼片段
//#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* @author liuguanglei ittimelinedotnet@gmail.com @version */
int main(int argc,char *argv[]) {
}
這里為了減輕工作量,使用Visual Studio 2019提供的代碼片段管理器來實現通過輸入配置的快捷鍵生成模板代碼。
首先自定義代碼片段文件MyCCode.snippet
<?xml version="1.0" encoding="utf-8"?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
<CodeSnippet Format="1.0.0">
<Header>
<Title>MyCCodeTemplate</Title>
<Shortcut>mcct</Shortcut>
<Description>c的標准模板代碼</Description>
<Author>Microsoft Corporation</Author>
<SnippetTypes>
<SnippetType>Expansion</SnippetType>
<SnippetType>SurroundsWith</SnippetType>
</SnippetTypes>
</Header>
<Snippet>
<Code Language="cpp">
<![CDATA[#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/*
@author liuguanglei ittimelinedotnet@gmail.com
@version
*/
int main(int argc, char *argv[])
{
system("pause");
return 0;
}]]>
</Code>
</Snippet>
</CodeSnippet>
</CodeSnippets>
然后進入Visual Studio 2019 菜單 工具->代碼片段管理器

選擇代碼片段管理器的語言為C++,然后選中My Code Snippets

把MyCCodeTemplate.snippet導入進來



導入成功之后新建源文件,在源文件中輸入快捷鍵mcct就可以生成如下的代碼片段
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* @author liuguanglei ittimelinedotnet@gmail.com @version */
int main(int argc, char* argv[]) {
system("pause");
return 0;
}
想要了解代碼片段的更多內容,請移步官網






















