[C語(yǔ)言]你真的了解C語(yǔ)言嗎之main函數(shù)(一)

有時(shí)候?qū)W過的知識(shí)反過頭來再看看總會(huì)有新理解,這可能就是所謂的溫故而知新吧,最近讀了一篇文章,覺得說的很對(duì),感興趣的可以讀一下。

http://norvig.com/21-days.html?norvig.com

就像文章中說的一樣,現(xiàn)在的人們都太急于求成,各種“XX天XXX語(yǔ)言從入門到大神”,大家有沒有想一下,這現(xiàn)實(shí)嗎?文章不是很長(zhǎng),建議大家都去看一下。

能夠點(diǎn)進(jìn)來看這篇文章的應(yīng)該都是懂C語(yǔ)言的,那么相信大家也都寫過很多代碼了,那么我們先來看一下下面這幾種main函數(shù)的定義,哪一個(gè)是正確的哪一個(gè)是錯(cuò)誤的。

main()

main(void)

main(int argc,char *argv[])

main(int argc,char **argv)

void main()

void main(void)

void main(int argc,char *argv[])

void main(int argc,char **argv)

int main()

int main(void)

int main(int argc,char *argv[])

int main(int argc,char **argv)

相信大家或多或少都見過或這寫過上面這這些main函數(shù)中的某一種,那么大家對(duì)這個(gè)C語(yǔ)言中最重要的一個(gè)函數(shù)有多少了解呢?有沒有深究過該怎樣寫和為什么這樣寫呢?其實(shí)講實(shí)話,我之前也是知其然不知所以然,因?yàn)橛X得會(huì)用就好了,為什么還要深入的去了解呢?所以現(xiàn)在還是菜鳥的一個(gè)原因吧。。。之所以寫這篇文章主要還是最近在做Linux程序開發(fā),這技術(shù)水平不到可不就得深入去了解一番嘛,作為一個(gè)菜鳥碼農(nóng)就得不斷去學(xué)習(xí)不斷去充實(shí)自己。

其實(shí)上面的這幾種main函數(shù)的定義在某種意義上來說是都是正確的,因?yàn)樵诓煌臉?biāo)準(zhǔn)下編譯都是可以編譯通過的,但是這不代表可以編譯通過我們就可以認(rèn)為是正確的寫法,既然有標(biāo)準(zhǔn)我們就要根據(jù)標(biāo)準(zhǔn)去寫,畢竟有了規(guī)則大家才可以寫出更好的代碼,下圖是C11標(biāo)準(zhǔn)中對(duì)main函數(shù)的說明,大家可以看一下。

image

<figcaption style="margin-top: 0.66667em; padding: 0px 1em; font-size: 0.9em; line-height: 1.5; text-align: center; color: rgb(153, 153, 153);">C11-main</figcaption>

下面我們來實(shí)際測(cè)試一下,看看編譯器在不同標(biāo)準(zhǔn)下如何處理上面的幾種情況,這里的測(cè)試環(huán)境是Ubuntu 16.04 & gcc 5.4.0,第一個(gè)測(cè)試代碼:

#include <stdio.h>
//不聲明main函數(shù)類型
//main() 與 main(void) 是等價(jià)的
main(void){
        printf("hello world!\r\n");
}

image

可以看出在C99與C11標(biāo)準(zhǔn)下編譯會(huì)提示一個(gè)警告,說默認(rèn)返回類型是int類型,但是在比較老的C90標(biāo)準(zhǔn)下是可以編譯通過和運(yùn)行的,我們?cè)倮^續(xù)測(cè)試下一個(gè):

#include <stdio.h>
//聲明main函數(shù)類型為void
//void main() 與 void main(void) 是等價(jià)的
void main(void){
        printf("hello world!\r\n");
}

image

可以看出在三種標(biāo)準(zhǔn)之下是都可以編譯通過的,但是這并不代表這就是正確的,我們?cè)賮砜匆幌翪11標(biāo)準(zhǔn)中是怎么講函數(shù)的返回值的:

image

我們可以這樣想,函數(shù)的返回值是判斷這個(gè)函數(shù)執(zhí)行是否成功的一個(gè)標(biāo)志,就像你自己寫某一個(gè)子函數(shù)時(shí),不同的情況可能會(huì)對(duì)應(yīng)不同的返回值,從而做出不同的處理,我們寫一個(gè)程序最終是要被系統(tǒng)調(diào)用的,那我們定義一個(gè)void類型,系統(tǒng)該如何判斷調(diào)用是否成功呢?所以這種寫法是禁止的,不推薦的,而且這種寫法在任何一個(gè)標(biāo)準(zhǔn)都沒有聲明過,我們繼續(xù)測(cè)試下一個(gè):

#include <stdio.h>
//聲明main函數(shù)類型為int
//int main() 與 int main(void) 是等價(jià)的
int main(void){
        printf("hello world!\r\n");
}

image

可以看到我們這里雖然聲明了int類型,卻沒有在程序中return一個(gè)返回值,但是在三種標(biāo)準(zhǔn)下都可以編譯通過并執(zhí)行,這是為什么呢?其實(shí)在前面圖中有說明,如果我們沒有寫return語(yǔ)句,程序是會(huì)在執(zhí)行到“}”時(shí)默認(rèn)返回0,0表示程序執(zhí)行成功,所以正確的寫法應(yīng)該如下:

#include <stdio.h>
//聲明main函數(shù)類型為int
//int main() 與 int main(void) 是等價(jià)的
int main(void){
        printf("hello world!\r\n");
     return 0;
}

在這幾個(gè)測(cè)試代碼中我們測(cè)試了不同類型的main函數(shù)在不同的標(biāo)準(zhǔn)下編譯,我們也查看了標(biāo)準(zhǔn)中是推薦怎樣寫的,所以我們以后寫的時(shí)候還是要根據(jù)標(biāo)準(zhǔn)來,有時(shí)候有一個(gè)良好的代碼規(guī)范在合作開發(fā)中是非常必要的。

但是這不是本篇文章要討論的重點(diǎn),之所以寫這篇文章是因?yàn)樽罱鼘懙臇|西需要處理一些命令行參數(shù),相信大家應(yīng)該都接觸過命令行參數(shù)吧,下面就給大家舉個(gè)簡(jiǎn)單的栗子:

gcc -std=c11 -o hello hello.c

沒錯(cuò)這就是我們剛剛用過的一條命令,gcc 是可執(zhí)行文件,就像我們前面編譯好的hello一樣,是可以直接執(zhí)行的,但是后面跟那么多參數(shù)是做什么的呢?其實(shí)簡(jiǎn)單來說就是根據(jù)不同的參數(shù)執(zhí)行不同的命令,就像std(standard)參數(shù)一樣,通過我們給出的不同參數(shù)執(zhí)行不同標(biāo)準(zhǔn)下的編譯標(biāo)準(zhǔn)。

那么這些參數(shù)是怎樣傳遞進(jìn)去的呢?這里就不得不提我們的main函數(shù)了,大家都知道m(xù)ain函數(shù)是整個(gè)程序的入口,程序的執(zhí)行就是從這里開始,所以當(dāng)然main函數(shù)就是我們的參數(shù)入口了,相信仔細(xì)閱讀的你肯定在前面的圖片中也有所發(fā)現(xiàn)了,這里我們?cè)賮砜匆幌拢?/p>

image

第一種就是不帶參數(shù)的,第二種是帶參數(shù)的,我們先上個(gè)簡(jiǎn)單代碼:

#include <stdio.h>
/**
 *@author imliubo
 *@brief  main函數(shù)傳參測(cè)試代碼
 *@param  argc    傳入的參數(shù)個(gè)數(shù),argc>=1
 *@param  *argv[] 傳入的參數(shù)的值,argv[0]=當(dāng)前執(zhí)行的程序名
**/
int main(int argc,char *argv[]){

  printf("argc:\t argv:\r\n");

  for(int i=0;i < argc;i++){
    printf("%d\t %s\r\n",i,argv[i]);
  }

 printf("argc total:%d\r\n",argc);
  return 0;
}

我們先來看一下不加任何參數(shù)運(yùn)行會(huì)打印什么:

image

可以看到我們不加任何參數(shù)時(shí),argc跟argv的值都是有變化的,因?yàn)槟J(rèn)的第一個(gè)參數(shù)是當(dāng)前程序的名(包含路徑),參數(shù)總個(gè)數(shù)為1,我們?cè)賮砜纯次覀兗訋讉€(gè)參數(shù)會(huì)打印什么:

image

可以看到我們?cè)诿钚兄械乃膫€(gè)參數(shù)都打印出來了,加上默認(rèn)的一共5個(gè)一個(gè)也沒有少,參數(shù)與參數(shù)之間是通過空格區(qū)分的(如果想將兩個(gè)參數(shù)合并成一個(gè)可以通過加上引號(hào)實(shí)現(xiàn)),那么我們?cè)撛鯓永眠@些參數(shù)呢?一個(gè)好的程序是可以配置的,不同參數(shù)會(huì)有不同的運(yùn)行結(jié)果,那么我們寫一個(gè)通過參數(shù)控制打印不同字符的程序,這里我們用最簡(jiǎn)單的方法去實(shí)現(xiàn):

#include <stdio.h>
#include <string.h>
/**
 *@author imliubo
 *@brief  main函數(shù)傳參測(cè)試代碼
 *@param  argc    傳入的參數(shù)個(gè)數(shù),argc>=1
 *@param  *argv[] 傳入的參數(shù)的值,argv[0]=當(dāng)前執(zhí)行的程序名
**/
int main(int argc,char *argv[]){

  for(int i=0;i < argc;i++){
    if(strcmp("imliubo",argv[i])==0){
      printf("Hi %s,how was going?\r\n",argv[i]);
    }
    if(strcmp("xiaomei",argv[i])==0){
      printf("What a beautiful girl!\r\n");
    }
  }
  return 0;
}

image

可以看出我們通過配置不同的參數(shù),程序是不同的執(zhí)行的結(jié)果,當(dāng)然這是一種菜鳥程序員的寫法,通過不斷的比較去執(zhí)行不同的代碼,加入我們?cè)僭谶@個(gè)參數(shù)前面加上一個(gè)帶有"-"修飾的字符,處理起來就要麻煩的多了,但是這種麻煩的問題肯定不只有我們才發(fā)現(xiàn),所以已經(jīng)有大神給我們寫好工具了。

平常我們?cè)谔幚砗芏喾彪s的命令行參數(shù)時(shí)都會(huì)使用getopt()函數(shù),比如像下面這條命令:

sudo ./nuwriter -m nand -d NUC972DF62Y.ini -t uboot -a 0x200 -w /home/imliubo/NUC972/WORKSPACE/COIDEA/BSP/03.uboot/nand_spl/u_boot_spl.bin -v

如果我們自己去解析這條命令中的參數(shù),費(fèi)時(shí)間不說,其中的邏輯還那么復(fù)雜,所以我們一般都是使用getopt()函數(shù)去解析,本篇文章就先到這,我會(huì)在下一篇文章中跟大家討論一下。

文章難免有錯(cuò)誤,如果有不對(duì)之處,還請(qǐng)及時(shí)指出我好及時(shí)改正。

歡迎關(guān)注我的專欄,我會(huì)不定期分享一些開發(fā)經(jīng)驗(yàn)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容