重溫C語言,這三十多個細節你把握住了?
文章目錄
前言
基本篇
1、編寫代碼文檔
難度指數:1顆星 / 細節指數:5顆星 / 重要指數:5顆星
C語言相對其他語言的優勢
難度指數:1顆星 / 細節指數:1顆星 / 重要指數:3顆星
C語言為什么不內置輸入輸出語句?
難度指數:2顆星 / 細節指數:4顆星 / 重要指數:3顆星
int main() 與 void main()的區別
難度指數:1顆星 / 細節指數:3顆星 / 重要指數:2顆星
可寫可不寫的花括號,憑什么要我寫?
難度指數:1顆星 / 細節指數:2顆星 / 重要指數:2顆星
`0==a` 與 `a==0`
難度指數:1顆星 / 細節指數:4顆星 / 重要指數:2顆星
T、‘T’、“T”的區別
難度指數:3顆星 / 細節指數:2顆星 / 重要指數:4顆星
short、long、unsigned
難度指數:2顆星 / 細節指數:2顆星 / 重要指數:1顆星
標準輸入輸出中的占位符
難度指數:2顆星 / 細節指數:2顆星 / 重要指數:3顆星
scanf讀取字符串
常用ascii碼
難度指數:1顆星 / 細節指數:3顆星 / 重要指數:4顆星
浮點數的比較大小
難度指數:1顆星 / 細節指數:2顆星 / 重要指數:2顆星
提升篇
刷新輸出
難度指數:2顆星 / 細節指數:3顆星 / 重要指數:4顆星
out of range
難度指數:4顆星 / 細節指數:5顆星 / 重要指數:5顆星
scanf 和 scanf_s
難度指數:3顆星 / 細節指數:1顆星 / 重要指數:2顆星
char* 與 char[n]
難度指數:1顆星 / 細節指數:3顆星 / 重要指數:3顆星
getchar() 與 putchar()
難度指數:3顆星 / 細節指數:2顆星 / 重要指數:2顆星
sprintf()做字符串拼接
難度指數:2顆星 / 細節指數:2顆星 / 重要指數:4顆星
設計原則:“需要知道”原則
難度指數:3顆星 / 細節指數:4顆星 / 重要指數:4顆星
作用域·鏈接
難度指數:2顆星 / 細節指數:1顆星 / 重要指數:2顆星
extern
難度指數:2顆星 / 細節指數:2顆星 / 重要指數:3顆星
argc 和argv
難度指數:2顆星 / 細節指數:2顆星 / 重要指數:3顆星
C/C++預編譯/條件編譯指令
難度指數:2顆星 / 細節指數:2顆星 / 重要指數:3顆星
指針篇
難度指數:5顆星 / 細節指數:5顆星 / 重要指數:5顆星
前言
好久不見啦朋友們。
前天參加了軟件設計師考試,說實話,有點emmm,但是我也發現很多基礎已經忘得差不多了,這就是傳說中的手生了嗎?
手生到什么地步?前天晚上幫我朋友改代碼,甚至連scanf輸入double類型數據用什么方式我都想不起來了。
所以,我就整理了一下我自己的學習路線。
江東子弟多才俊,卷土重來未可知!
拿著《C Primer Plus》梳理了一遍,發現還真的有不少細節平時沒有注意到,或者是沒有刻意的去注意。
基本篇
1、編寫代碼文檔
(寫代碼不寫文檔,拖出去打屎)
最開始接觸到代碼文檔不知道是什么時候了,但是讓我想寫代碼文檔絕對是在pycharm上。
很方便,打三個引號,一個回車,什么都給你準備好了。
然后我就想在VS上也找到類似的功能。起初沒找到,后來誤打誤撞試出來了:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) { ///
1
2
3
4
5
6
7
吶,看明白不?畫三個杠,不用回車,兩個
C語言相對其他語言的優勢
(連C語言的優勢都不清楚,學來干什么?)
1、目前我們所學習的各種語言,基本都離不開C語言的影子,所謂萬變不離其宗,就是這個道理。
2、C語言快,不接受反駁。不要說什么匯編更快,寫個我看看。
3、可移植性強。就拿嵌入式這一塊來說,哪個語言能輕松移植?
4、細節把控到位。C語言給了程序員極大的細節操作權限,連內存分配都給了。只是我們自己把握不住而已,C語言的水太深了。
C語言為什么不內置輸入輸出語句?
別說輸入輸出了,不包任何頭文件,我不知道還能寫什么C代碼。
為什么要這樣呢?像Python那樣都內置了不好嗎?
這也是C語言為什么能做嵌入式,而Python做不了的一個重要原因。
C語言的一個基本設計原則是避免不必要的成分
。
我們不可否認,并不是所有項目都需要輸入輸出的。
兒童節快樂呀各位
int main() 與 void main()的區別
不知道有多少小伙伴接觸過這個 void main(),反正我剛開始學C的時候,那個老師是教我們寫這個的,當時就跟我們說,如果沒有什么返回值,就寫這個就好啦。
后來,遇到我的授業恩師的時候,他叫我們說,不要寫void main()了,打開編輯器,寫完頭文件依賴之后,先把下面這個框架寫上:
int main(){ return 0; }
1
2
3
4
就算沒有什么返回值,也寫一個return 0;,錯不了的。
有些編譯器會允許void main()的形式,但是還沒有任何標準考慮接受它,所以編譯器可以不接受這種形式,這就是一個在平臺移植中存在的一個隱患。
多寫一行return 0;很難嗎?
可寫可不寫的花括號,憑什么要我寫?
有的小伙伴可能不知道,在循環語句、分支語句中,如果代碼塊兒只有一行的情況下(或者循環下面只有一個分支語句),則那個花括號是可寫可不寫的。
比方說:
while(1) printf('1\n')
1
2
那這個花括號寫不寫呢?
不寫會有什么好處?
1、代碼看起來行數會短一丟丟
2、少寫兩個括號
不寫有什么壞處?
當下基本不會有什么壞處,當下咱的頭腦坑定是清醒的,知道為什么不寫。
但是修改代碼的時候呢?如果要在這種循環下動刀,卻又忽略了這個括號呢?
我前兩天就遇到了,浪費我五分鐘去調試。
所以啊,又不是說什么很必要的,為什么不寫?寫兩個括號會累著?
0==a 與 a==0
這個又屬于那種舉手之勞,但是暴雷的時候不知死活的問題了。
一般這個細節老師在講分支循環的時候都會說吧,如果少寫了一個等號,0=a是會報錯的,但是a=0是會崩潰的。
不過現在還好,有的編輯器就會警告,前提是要使用夠好的編輯器,碧如VS。
像我以前用TXT編程的時候,這個問題就只能靠自己去挖掘了。
細節之處見真章。
T、‘T’、“T”的區別
這就涉及到字符和字符串的概念了。
這也是一段時間不敲C代碼會很容易忘掉的一個點。
首先,T如果經過賦值,它既是一個變量。否則它什么也不是。
其次,‘T’是一個字符,一個char,不是一個字符串。
緊接著,“T”是一個字符串,不是一個char。
什么是字符串,可以理解為char的數組,不過在字符串結尾的時候會帶上一個‘\0’。
short、long、unsigned
不要再覺得這三個很神秘了,它們只不過是修飾詞而已了。
short,
可能
占用比int類型更少的空間,用于僅需小數值的場合,可以簡寫為short。同int 類型一樣,是一種有符號類型。
long,
可能
占用比int類型更大的空間,用于使用大數值的場合,可簡寫為long。同int類型一樣,是一種有符號類型。
long long,
可能
占用比long更大的空間,用于更大數值的場合,可簡寫為long long,同int類型一樣,是一種有符號類型。
unsigned,用于只使用非負的場合。將原本分配給負數的空間大小都分配給了正數。
標準輸入輸出中的占位符
%d —— 以帶符號的十進制形式輸出整數 %o —— 以無符號的八進制形式輸出整數 %x —— 以無符號的十六進制形式輸出整數 %u —— 以無符號的十進制形式輸出整數 %c —— 以字符形式輸出單個字符 %s —— 輸出字符串 %f —— 以小數點形式輸出單、雙精度實數 %e —— 以標準指數形式輸出單、雙精度實數 %g —— 選用輸出寬度較小的格式輸出實數
1
2
3
4
5
6
7
8
9
如果是打印short,用%u,打印long,用%ld,以免在移植過程中造成不必要的麻煩。
scanf讀取字符串
和讀取單個字符不同,讀取字符串的時候,是不需要加上&符號的。
常用ascii碼
其實上網一搜就有了,但是有的比較重要的還是要記一下的。
ESC -- 27 0 -- 48 A -- 65 a -- 97
1
2
3
4
浮點數的比較大小
對于浮點數的比較,只能用<和>,舍入誤差可能造成兩個邏輯上應該相等的數不相等。。
提升篇
刷新輸出
printf()什么時候真正把輸出傳送給屏幕?printf()語句將輸出傳送給一個被稱為緩沖區的中介存儲區域,緩沖區中的數據再不斷地被傳送給函數。
標準C規定在以下情況下講緩存區內容輸送給屏幕:
1、緩沖區滿
2、遇到換行符
3、后面跟了一個scanf語句
可能在平時看來沒有什么關系,但是我們在寫服務器代碼的時候就會有這種問題出來,有時候會導致消息隊列被卡死,有時候會導致數據無法及時的被排出。
這里拓展一下緩沖區,為什么需要緩沖區?
首先,將若干個字符作為一個塊傳輸比逐個發送這些字符耗費的時間少。
其次,如果輸入有誤,就可以使用回刪來更正錯誤。
當最終按下回車簡單的時候,就可以發送正確的輸入。
緩沖分為兩類,完全緩沖I/O和行緩沖I/O,對完全緩沖輸入來說,緩沖區滿時被清空,這種類型的緩沖常出現在文件傳輸中。緩沖區的大小取決于操作系統。
對于行緩沖來說,遇到一個換行符就將清空緩沖區,鍵盤輸入是標準的行緩沖,因此按下回車鍵將清空緩沖區。
out of range
我就不吭聲兒,哪個寫C/C++的朋友沒有遇到過這個問題。
越界。
一個潛在的問題是:出于執行速度考慮,C并不檢查您是否使用了正確的數值下標,當程序運行的時候,這些語句把數據放在可能被其他數據使用到的位置上,因而可能破壞程序的結果,甚至使得程序結果崩潰。
scanf 和 scanf_s
在使用VS的時候,會發現編譯器不通過scanf,給的理由是不安全、即使已經#include
這時候,它就會推薦我們去使用scanf_s。
解決方法一:
點擊項目->項目屬性,點開屬性頁面
點擊C/C++ -> 預處理器 -> 預處理器定義 -> 點擊右側的下拉列表 -> 點擊下拉列表里的<編輯>
在預處理器定義中添加字段 _CRT_SECURE_NO_WARNINGS
然后點擊確定,就可以使用scanf了
但是僅限于這一個項目,其他的項目還是不能使用,因此需要對所有要使用scanf的項目進行逐個修改
方法二:使用scanf_s
scanf()不會檢查輸入邊界,可能造成數據溢出。
scanf_s()會進行邊界檢查。
因為帶“_s”后綴的函數是為了讓原版函數更安全,傳入一個和參數有關的大小值,避免引用到不存在的元素,防止hacker利用原版的不安全性(漏洞)黑掉系統。
scanf_s()參數與scanf()不同:
例如scanf_s(“%s”,&name,n),整形n為name類型的大小,如果name是數組,那n就是該數組的大小。
char* 與 char[n]
這二者,最大的區別就在于,一個是動態空間分配,一個是靜態空間分配。
如果說,使用了char *a,這時候要使用a,要手動分配空間。
而且,當我們使用sizeof(a)的時候,得出的結果是指針的大小,這是一個坑。
要獲取a的大小,使用len()函數。
這里把后面一個問題一并寫進來吧,
結構體中是應該放 char* 還是char[] 呢?
要知道,結構體不為字符串分配任何存儲空間,所以自己掂量掂量。
如果沒有什么特殊需求,還是放char[],如果要定制,那就char*。
反正這倆我都試過,一個是定長包,相對簡單,一個是不定長包,雖然困難了點,可以克服。
getchar() 與 putchar()
getchar的用法
getchar()是stdio.h中的庫函數,它的作用是從stdin流中讀入一個字符,也就是說,如果stdin有數據的話不用輸入它就可以直接讀取了,第一次getchar()時,確實需要人工的輸入,但是如果你輸了多個字符,以后的getchar()再執行時就會直接從緩沖區中讀取了。
實際上是 輸入設備->內存緩沖區->程序getchar
putchar的用法
(1)輸出:putchar函數只能用于單個字符的輸出,向終端輸出一個字符,且一次只能輸出一個字符。
(2)格式:對于變量來說,格式為:putchar(ch);對于常量來說,格式為:putchar(‘ch’),對于轉義字符來說,格式為:putchar(’\n’)。
sprintf()做字符串拼接
字符串的處理一直是很重要的問題,C語言中的字符串拼接又不像Python里面直接一個加號就能解決的。
那么要怎么處理呢?這里采用 sprintf() 的方式來做這件事情。
int sprintf(char *str, const char *format, ...);
1
參數釋義:
str -- 這是指向一個字符數組的指針,該數組存儲了 C 字符串。 format -- 這是字符串,包含了要被寫入到字符串 str 的文本。它可以包含嵌入的 format 標簽,format 標簽可被隨后的附加參數中指定的值替換,并按需求進行格式化。format 標簽屬性是 %[flags][width][.precision][length]specifier,具體講解如下:
1
2
不多說,上案例:
char s[40]; sprintf(s,"%s%d%c","test",1,'2'); /*第一個參數就是指向要寫入的那個字符串的指針,剩下的就和printf()一樣了
1
2
設計原則:“需要知道”原則
為什么我覺得這個部分值得這么多星星呢?可能有的朋友覺得不值。
寫幾個項目,再優化,就知道了。
“需要知道”原則,類似于“單一職責原則”,盡可能保持每個函數內部工作對該函數的私密性,只共享那些需要共享的變量。
作用域·鏈接
一個C變量具有以下鏈接之一:外部鏈接、內部鏈接或空鏈接。
具有代碼塊作用域或者函數原型作用域的變量具有空鏈接,意味著它們是由其定義所在的代碼塊或函數原型所私有。
重點來了:具有文件作用域的變量可能有內部鏈接或外部鏈接,一個具有外部鏈接的變量可以在一個多文件的程序的任何地方使用,一個具有內部鏈接的變量可以在一個文件的任何地方使用。
那怎樣知道一個文件作用域變量具有內部鏈接還是外部鏈接?可以看看在外部定義中是否使用了存儲類說明符static。
extern
把變量的定義聲明放在所有函數之外,即創建了一個外部變量。為了使程序更加清晰,可以在使用外部變量的函數中通過使用extern關鍵字來再次聲明它。
如果變量是在別的文件中定義的,那么使用extern來聲明該變量就是必須的。
argc 和argv
在Linux底下編程的時候,經常會看到如下的一行代碼:
int main(int argc,char*argv[]){}
1
有時候,這個argv還會在main函數實現中被用到,那么就會有小伙伴不知道是干嘛用的,或者說知道是干嘛用的,不知道怎么用。
我也困惑過,所以寫下來。
main(int argc,char *argv[ ]) argv為指針的指針 argc為整數 char **argv or: char *argv[] or: char argv[][]
1
2
3
4
5
6
7
假設程序的名稱為CX,
當只輸入CX,則由操作系統傳來的參數為:
argc=1,表示只有一程序名稱。
argc只有一個元素,argv[0]指向輸入的程序路徑及名稱:./CX
當輸入==./CX CanShu_1==,有一個參數,則由操作系統傳來的參數為:argc=2,表示除了程序名外還有一個參數。
argv[0]指向輸入的程序路徑及名稱。 argv[1]指向參數para_1字符串。
1
2
當輸入==./CX CanShu_1 CanShu_2== 有2個參數,則由操作系統傳來的參數為:argc=3,表示除了程序名外還有2個參數。
argv[0]指向輸入的程序路徑及名稱。 argv[1]指向參數para_1字符串。 argv[2]指向參數para_2字符串。
1
2
3
4
5
以此類推.
C/C++預編譯/條件編譯指令
實在不想再長篇大論了,偷個懶吧:講通C/C++預編譯/條件編譯指令 #ifdef,#ifndef,#endif,#define,…
指針篇
開發成長之路(3)-- C語言從入門到開發(講明白指針和引用,鏈表很難嗎?)
精品專欄打造計劃
C 語言 Python
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。