日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

DEBUG、void、NULL、C库和API、临时匿名变量、main函数

發(fā)布時間:2023/12/20 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 DEBUG、void、NULL、C库和API、临时匿名变量、main函数 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.


一、程序調試的debug宏

1、程序調試的常見方案

單步調試、裸機LED調試、打印信息、log文件

  • 利用調試器進行單步調試(譬如IDE中,Jlink)適用于新手,最大的好處就是直觀,能夠幫助找到問題。缺點是限制性大、速度慢。
  • 裸機使用LED、蜂鳴器等硬件調試,適合單片機裸機程序。
  • printf函數(shù)打印調試,比較常用,作為程序員必須學會使用打印信息調試。好處是具有普遍性,幾乎在所有的情況下都能用。
  • log文件(日志文件)是系統(tǒng)運行過程中在特定時候會打印一些調試信息,日志文件記錄下來這些調試信息以供后續(xù)查找追查問題。適合于系統(tǒng)級或者大型程序的調試。

2、打印信息不能太多也不能太少

  • 調試信息太少會不夠信息找到問題所在。
  • 調試信息太多會有大量的無用的信息淹沒有用信息,導致有用信息無法看見,等于沒有。

3、調試(DEBUG)版本和發(fā)行(RELEASE)版本的區(qū)別

  • DEBUG版本就是包含了調試信息輸出的版本,在程序測試過程中會發(fā)布debug版本,這種版本的程序運行時會打印出來調試信息/log文件,這些信息可以輔助測試人員判斷程序的問題所在。DEBUG版本的壞處是輸出調試信息占用了系統(tǒng)資源,拖慢了系統(tǒng)運行速度。因此DEBUG版本的性能低于RELEASE版本。
  • RELEASE版本就是最終的發(fā)布版本,相較于DEBUG版本的功能代碼是一樣的,但是去掉了所有的調試信息。適合最終測試通過要發(fā)布的程序,因為去掉了調試信息所以程序的運行效率要更高。
  • DEBUG和RELASE版本其實是一套源代碼。源代碼中是有很多的打印調試信息的語句的,如何來控制生成DEBUG和RELEEASE版本?靠條件編譯,靠一個宏。

4、debug宏的實現(xiàn)原理

(1)DEBUG宏大概的原理是:

#ifdef DEBUG #define dbg() printf() #else #define dbg() #endif

(2)工作方式

  • 如果要輸出DEBUG版本,則在條件編譯語句前加上#define DEBUG即可,這樣程序中的調試語句dbg()就會被替換成printf從而輸出;
  • 如果要輸出RELEASE版本則去掉#define DEBUG,則dbg()會被替換成空,則程序中所有的dbg()語句無效,這樣的程序編譯時就會生成沒有任何調試信息的代碼。


二、debug宏的使用方法

1、應用程序中DEBUG宏

#ifdef DEBUG #define DBG(...) fprintf(stderr, " DBG(%s, %s(), %d): ", __FILE__, __FUNCTION__, __LINE__); fprintf(stderr, __VA_ARGS__) #else #define DBG(...) #endif

  • 注:__FILE__等是C語言中的預定義宏,具有特殊的含義,譬如__FILE__表示當前正在編譯的c文件的文件名。

2、內核中的DEBUG宏

#ifdef DEBUG_S3C_MEM #define DEBUG(fmt, args...) printk(fmt, ##args) #else #define DEBUG(fmt, args...) do {} while (0) #endif

三、void類型的本質

1、C語言屬強類型語言

(1)強類型語言和弱類型語言

  • 強類型語言中所有的變量都有自己固定的類型,這個類型有固定的內存占用,有固定的解析方法;
  • 弱類型語言中沒有類型的概念,所有變量全都是一個類型(一般都是字符串的),程序在用的時候再根據(jù)需要來處理變量。

(2)C語言是典型的強類型語言

  • C語言中所有的變量都有明確的類型。
  • 因為C語言中的一個變量都要對應內存中的一段內存,編譯器需要這個變量的類型來確定這個變量占用內存的字節(jié)數(shù)和這一段內存的解析方法。

2、數(shù)據(jù)類型的本質含義

(1)數(shù)據(jù)類型決定變量的內存占用數(shù),和內存的解析方法。

(2)如果一個變量沒有確定的類型(就是所謂的無類型)會導致編譯器無法給這個變量分配內存,也無法解析這個變量對應的內存。因此得出結論不可能有沒有類型的變量。

(3)C語言中可以有沒有類型的內存。

  • 在內存還沒有和具體的變量相綁定之前,內存就可以沒有類型。
  • 實際上純粹的內存就是沒有類型的,內存只是因為和具體的變量相關聯(lián)后才有了確定的類型(其實內存自己本身是不知道的,而編譯器知道,我們程序在使用這個內存時知道類型所以會按照類型的含義去進行內存的讀和寫)。

3、void類型的本質

(1)void類型的正確的含義是:不知道類型,不確定類型,還沒確定類型。

(2)void a;

  • 定義了一個void類型的變量,含義就是說a是一個變量,而且a肯定有確定的類型,只是目前還不知道a的類型,還不確定,所以標記為void。

4、為什么需要void類型

(1)什么情況下需要void類型?

  • 在描述一段還沒有具體使用的內存時需要使用void類型。

(2)void的一個典型應用案例就是malloc的返回值。

  • malloc函數(shù)向系統(tǒng)堆管理器申請一段內存給當前程序使用,malloc返回的是一個指針,這個指針指向申請的那段內存。
  • malloc剛申請的這段內存尚未用來存儲數(shù)據(jù),malloc函數(shù)也無法預知這段內存將來被存放什么類型的數(shù)據(jù),所以malloc無法返回具體類型的指針,解決方法就是返回一個void *類型,告訴外部返回的是一段干凈的內存空間,尚未確定類型。
  • 所以在malloc之后可以給這段內存讀寫任意類型的數(shù)據(jù)。

(3)void *類型的指針指向的內存是尚未確定類型的,因此我們后續(xù)可以使用強制類型轉換強行將其轉為各種類型。

  • 這就是void類型的最終歸宿,就是被強制類型轉換成一個具體類型。

(4)void類型使用時一般都是用void *,而不是僅僅使用void。



四、語言中的NULL

1、NULL在C/C++中的標準定義

(1)NULL不是C語言關鍵字,本質上是一個宏定義

#ifdef _cplusplus // 條件編譯 #define NULL 0 #else #define NULL (void *)0 // 這里對應C語言的情況 #endif

  • C++的編譯環(huán)境中,編譯器預先定義了一個宏_cplusplus,程序中可以用條件編譯來判斷當前的編譯環(huán)境是C++的還是C的。
  • NULL的本質是0,但是這個0不是當一個數(shù)字解析,而是當一個內存地址來解析的,這個0其實是0x00000000,代表內存的0地址。(void *)0這個整體表達式表示一個指針,這個指針變量本身占4字節(jié),地址在哪里取決于指針變量本身,但是這個指針變量的值是0,也就是說這個指針變量指向0地址(實際是0地址開始的一段內存)。

2、從指針角度理解NULL的本質

(1)為什么要讓一個野指針指向內存地址0處?

  • 主要是因為在大部分的CPU中,內存的0地址處都不是可以隨便訪問的(一般都是操作系統(tǒng)嚴密管控區(qū)域,所以應用程序不能隨便訪問)。
  • 所以野指針指向這個區(qū)域可以保證野指針不會造成誤傷。如果程序無意識的解引用指向0地址處的野指針則會觸發(fā)段錯誤。

3、為什么需要NULL

  • 讓野指針指向0地址處安全。
  • 一個特殊標記。
int *p = NULL; // 定義p時立即初始化為NULL p = xx; if (NULL != p) {*p // 在確認p不等于NULL的情況下才去解引用p } p = NULL // 用完之后p再次等于NULL
  • 一般比較一個指針和NULL是否相等不寫成if (p == NULL),而寫成if (NULL == p)。
  • 原因是第一種寫法中如果不小心把==寫成了=,則編譯器不會報錯,但是程序的意思完全不一樣了;而第二種寫法如果不小心把==寫成了=則編譯器會發(fā)現(xiàn)并報錯。

4、注意不要混用NULL與'\0'

  • '\0'是一個轉義字符,對應的ASCII編碼值是0,本質就是0;
  • '0'是一個字符,對應的ASCII編碼值是48,本質是48;
  • 0是一個數(shù)字,它就是0,本質就是0;
  • NULL是一個表達式,是強制類型轉換為void *類型的0,本質是0。
  • '\0'用法是C語言字符串的結尾標志,一般用來比較字符串中的字符以判斷字符串有沒有到頭;
  • '0'是字符0,對應0這個字符的ASCII編碼,一般用來獲取0的ASCII碼值;
  • 0是數(shù)字,一般用來比較一個int類型的數(shù)字是否等于0;
  • NULL是一個表達式,一般用來比較指針是否是一個野指針。


五、運算中的臨時匿名變量

1、C語言和匯編的區(qū)別

  • 語言叫高級語言,匯編語言叫低級語言。
  • 低級語言的意思是匯編語言和機器操作相對應,匯編語言只是CPU的機器碼的助記符,用匯編語言寫程序必須擁有機器的思維。因為不同的CPU設計時指令集差異很大,因此用匯編編程的差異很大。
  • 高級語言(C語言)它對低級語言進行了封裝(C語言的編譯器來完成),給程序員提供了一個靠近人類思維的一些語法特征,人類不用過于考慮機器原理,而可以按照自己的邏輯原理來編程。譬如數(shù)組、結構體、指針……
  • 更高級的語言如java、C#等只是進一步強化了C語言提供的人性化的操作界面語法,在易用性上、安全性上進行了提升。

2、C語言的一些“小動作”

(1)高級語言中有一些元素是機器中沒有的。

(2)高級語言在運算中允許我們大跨度的運算。

  • 意思就是低級語言中需要好幾步才能完成的一個運算,在高級語言中只要一步即可完成。
  • 譬如C語言中一個變量i要加1,在C中只需要i++即可,看起來只有一句代碼。
  • 但實際上翻譯到匯編階段需要3步才能完成:第1步從內存中讀取i到寄存器,第2步對寄存器中的i進行加1,第3步將加1后的i寫回內存中的i。

3、使用臨時變量來理解強制類型轉換


4、使用臨時變量來理解不同數(shù)據(jù)類型之間的運算


六、c庫函數(shù)和API函數(shù)的關系

1、操作系統(tǒng)的調用通道:API

  • 操作系統(tǒng)負責管理和資源調配,應用程序負責具體的直接勞動,之間的接口就是API函數(shù)。
  • 當應用程序需要使用系統(tǒng)資源(譬如內存、譬如CPU、譬如硬件操作)時就通過API向操作系統(tǒng)發(fā)出申請,然后操作系統(tǒng)響應申請幫助應用程序執(zhí)行功能。

2、C庫函數(shù)和API的關系

  • API只提供極簡單沒有任何封裝的服務函數(shù),這些函數(shù)應用程序是可用的,但是不太好用。對這個API進行二次封裝,把它變得好用一些,于是就成了C庫函數(shù)。
  • 完成一個功能,有相應的庫函數(shù)可以完成,也有API可以完成,用哪個都行。譬如讀寫文件,API的接口是open write read close;庫函數(shù)的接口是fopen fwrite fread fclose。fopen本質上是使用open實現(xiàn)的,只是進行了封裝。封裝肯定有目的(添加緩沖機制)。

3、不同平臺(windows、linux、裸機)下庫函數(shù)的差異

  • 不同操作系統(tǒng)的API是不同的,完成一個任務所調用的API不同。
  • 不同操作吸人的庫函數(shù)也是不同的,但相似性高一些。在一個操作系統(tǒng)上寫的應用程序不可能直接在另一個操作系統(tǒng)上面編譯運行,要進行移植。
  • 跨操作系統(tǒng)可移植平臺,譬如QT、譬如Java語言。


七、main函數(shù)返回給誰

1、函數(shù)為什么需要返回值

  • 函數(shù)在設計的時候設計了參數(shù)和返回值,參數(shù)是函數(shù)的輸入,返回值是函數(shù)的輸出。
  • 因為函數(shù)需要對外輸出數(shù)據(jù)(實際上是函數(shù)運行的一些結果值)因此需要返回值。
  • 函數(shù)被另一個函數(shù)所調用,返回值作為函數(shù)式的值返回給調用這個函數(shù)的地方。
  • 函數(shù)的返回值就是給調用它的人返回一個值。

2、main函數(shù)被誰調用

  • main函數(shù)是特殊的。C語言規(guī)定main函數(shù)是整個程序的入口。其他的函數(shù)只有直接或間接被main函數(shù)所調用才能被執(zhí)行。
  • main函數(shù)從某種角度來講代表了當前這個程序,或者說代表了整個程序。main函數(shù)的開始意味著整個程序開始執(zhí)行,main函數(shù)的結束返回意味著整個程序的結束。
  • 誰執(zhí)行了這個程序,誰就調用了main。
  • 誰執(zhí)行了程序?或者說程序有哪幾種被調用執(zhí)行的方法?

3、linux下一個新程序執(zhí)行的本質

(1)linux中在命令行中去./xx執(zhí)行一個可執(zhí)行程序。

(2)可以通過shell腳本來調用執(zhí)行一個程序。

(3)可以在程序中去調用執(zhí)行一個程序(fork exec)。

(4)總結

  • 有多種方法都可以執(zhí)行一個程序,但是本質上是相同的。
  • linux中一個新程序的執(zhí)行本質上是一個進程的創(chuàng)建、加載、運行、消亡。
  • linux中執(zhí)行一個程序其實就是創(chuàng)建一個新進程然后把這個程序丟進這個進程中去執(zhí)行直到結束。新進程是被誰開啟?在linux中進程都是被它的父進程fork出來的。

(5)分析

  • 命令行本身就是一個進程,在命令行底下去./xx執(zhí)行一個程序,其實這個新程序是作為命令行進程的一個字進程去執(zhí)行的。即一個程序被它的父進程所調用。
  • main函數(shù)返回給調用這個函數(shù)的父進程。父進程調用子進程來執(zhí)行一個任務,然后子進程執(zhí)行完后通過main函數(shù)的返回值返回給父進程一個答復。這個答復一般是表示子進程的任務執(zhí)行結果完成了還是錯誤了。(0表示執(zhí)行成功,負數(shù)表示失敗)

八、argc、argv與main函數(shù)的傳參

1、誰給main函數(shù)傳參

  • 其父進程給main函數(shù)傳參,并且接收main的返回值。

2、為什么需要給main函數(shù)傳參

(1)main函數(shù)不傳參是可以的,也就是說父進程調用子程序并且給子程序傳參不是必須的。 int main(void)這種形式就表示我們認為不必要給main傳參。

(2)有時候我們希望程序有一種靈活性,所以選擇在執(zhí)行程序時通過傳參來控制程序中的運行,達到不需要重新編譯程序就可以改變程序運行結果的效果。

3、給main傳參是怎樣實現(xiàn)的?

(1)給main傳參通過argc和argv這兩個C語言預訂的參數(shù)來實現(xiàn)

(2)argc是int類型,表示運行程序的時候給main函數(shù)傳遞了幾個參數(shù);

(3)argv是一個字符串數(shù)組,這個數(shù)組用來存儲多個字符串,每個字符串就是我們給main函數(shù)傳的一個參數(shù)。

  • argv[0]就是我們給main函數(shù)的第一個傳參,argv[1]就是傳給main的第二個參數(shù)····

4、給main傳參是怎樣實現(xiàn)的?

  • 程序調用有各種方法但是本質上都是父進程fork一個子進程,然后字進程和一個程序綁定起來去執(zhí)行(exec函數(shù)族),我們在exec的時候可以給他同時傳參。
  • 程序調用時可以被傳參(也就是main的傳參)是操作系統(tǒng)層面的支持完成的。

5、給main傳參要注意什么

  • main函數(shù)傳參都是通過字符串傳進去的。
  • 程序被調用時傳參,各個參數(shù)之間是通過空格來間隔的。
  • 在程序內部如果要使用argv,那么一定要先檢驗argc。



1)NULL不是C語言關鍵字,本質上是一個宏定義

#ifdef _cplusplus // 條件編譯 #define NULL 0 #else #define NULL (void *)0 // 這里對應C語言的情況 #endif

  • C++的編譯環(huán)境中,編譯器預先定義了一個宏_cplusplus,程序中可以用條件編譯來判斷當前的編譯環(huán)境是C++的還是C的。
  • NULL的本質是0,但是這個0不是當一個數(shù)字解析,而是當一個內存地址來解析的,這個0其實是0x00000000,代表內存的0地址。(void *)0這個整體表達式表示一個指針,這個指針變量本身占4字節(jié),地址在哪里取決于指針變量本身,但是這個指針變量的值是0,也就是說這個指針變量指向0地址(實際是0地址開始的一段內存)。


2、從指針角度理解NULL的本質

(1)為什么要讓一個野指針指向內存地址0處?

  • 主要是因為在大部分的CPU中,內存的0地址處都不是可以隨便訪問的(一般都是操作系統(tǒng)嚴密管控區(qū)域,所以應用程序不能隨便訪問)。
  • 所以野指針指向這個區(qū)域可以保證野指針不會造成誤傷。如果程序無意識的解引用指向0地址處的野指針則會觸發(fā)段錯誤。


3、為什么需要NULL

  • 讓野指針指向0地址處安全。
  • 一個特殊標記。

int *p = NULL; // 定義p時立即初始化為NULL p = xx; if (NULL != p) {*p // 在確認p不等于NULL的情況下才去解引用p } p = NULL // 用完之后p再次等于NULL

  • 一般比較一個指針和NULL是否相等不寫成if (p == NULL),而寫成if (NULL == p)。
  • 原因是第一種寫法中如果不小心把==寫成了=,則編譯器不會報錯,但是程序的意思完全不一樣了;而第二種寫法如果不小心把==寫成了=則編譯器會發(fā)現(xiàn)并報錯。

4、注意不要混用NULL與'\0'

  • '\0'是一個轉義字符,對應的ASCII編碼值是0,本質就是0;
  • '0'是一個字符,對應的ASCII編碼值是48,本質是48;
  • 0是一個數(shù)字,它就是0,本質就是0;
  • NULL是一個表達式,是強制類型轉換為void *類型的0,本質是0。
  • '\0'用法是C語言字符串的結尾標志,一般用來比較字符串中的字符以判斷字符串有沒有到頭;
  • '0'是字符0,對應0這個字符的ASCII編碼,一般用來獲取0的ASCII碼值;
  • 0是數(shù)字,一般用來比較一個int類型的數(shù)字是否等于0;
  • NULL是一個表達式,一般用來比較指針是否是一個野指針。

總結

以上是生活随笔為你收集整理的DEBUG、void、NULL、C库和API、临时匿名变量、main函数的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內容還不錯,歡迎將生活随笔推薦給好友。