[C语言]你真的了解C语言吗之main函数(一)

有时候学过的知识反过头来再看看总会有新理解,这可能就是所谓的温故而知新吧,最近读了一篇文章,觉得说的很对,感兴趣的可以读一下。

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

就像文章中说的一样,现在的人们都太急于求成,各种“XX天XXX语言从入门到大神”,大家有没有想一下,这现实吗?文章不是很长,建议大家都去看一下。

能够点进来看这篇文章的应该都是懂C语言的,那么相信大家也都写过很多代码了,那么我们先来看一下下面这几种main函数的定义,哪一个是正确的哪一个是错误的。

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函数中的某一种,那么大家对这个C语言中最重要的一个函数有多少了解呢?有没有深究过该怎样写和为什么这样写呢?其实讲实话,我之前也是知其然不知所以然,因为觉得会用就好了,为什么还要深入的去了解呢?所以现在还是菜鸟的一个原因吧。。。之所以写这篇文章主要还是最近在做Linux程序开发,这技术水平不到可不就得深入去了解一番嘛,作为一个菜鸟码农就得不断去学习不断去充实自己。

其实上面的这几种main函数的定义在某种意义上来说是都是正确的,因为在不同的标准下编译都是可以编译通过的,但是这不代表可以编译通过我们就可以认为是正确的写法,既然有标准我们就要根据标准去写,毕竟有了规则大家才可以写出更好的代码,下图是C11标准中对main函数的说明,大家可以看一下。

image

C11-main

下面我们来实际测试一下,看看编译器在不同标准下如何处理上面的几种情况,这里的测试环境是Ubuntu 16.04 & gcc 5.4.0,第一个测试代码:

#include 
//不声明main函数类型
//main() 与 main(void) 是等价的
main(void){
        printf("hello world!\r\n");
}

image

可以看出在C99与C11标准下编译会提示一个警告,说默认返回类型是int类型,但是在比较老的C90标准下是可以编译通过和运行的,我们再继续测试下一个:

#include 
//声明main函数类型为void
//void main() 与 void main(void) 是等价的
void main(void){
        printf("hello world!\r\n");
}

image

可以看出在三种标准之下是都可以编译通过的,但是这并不代表这就是正确的,我们再来看一下C11标准中是怎么讲函数的返回值的:

image

我们可以这样想,函数的返回值是判断这个函数执行是否成功的一个标志,就像你自己写某一个子函数时,不同的情况可能会对应不同的返回值,从而做出不同的处理,我们写一个程序最终是要被系统调用的,那我们定义一个void类型,系统该如何判断调用是否成功呢?所以这种写法是禁止的,不推荐的,而且这种写法在任何一个标准都没有声明过,我们继续测试下一个:

#include 
//声明main函数类型为int
//int main() 与 int main(void) 是等价的
int main(void){
        printf("hello world!\r\n");
}

image

可以看到我们这里虽然声明了int类型,却没有在程序中return一个返回值,但是在三种标准下都可以编译通过并执行,这是为什么呢?其实在前面图中有说明,如果我们没有写return语句,程序是会在执行到“}”时默认返回0,0表示程序执行成功,所以正确的写法应该如下:

#include 
//声明main函数类型为int
//int main() 与 int main(void) 是等价的
int main(void){
        printf("hello world!\r\n");
     return 0;
}

在这几个测试代码中我们测试了不同类型的main函数在不同的标准下编译,我们也查看了标准中是推荐怎样写的,所以我们以后写的时候还是要根据标准来,有时候有一个良好的代码规范在合作开发中是非常必要的。

但是这不是本篇文章要讨论的重点,之所以写这篇文章是因为最近写的东西需要处理一些命令行参数,相信大家应该都接触过命令行参数吧,下面就给大家举个简单的栗子:

gcc -std=c11 -o hello hello.c

没错这就是我们刚刚用过的一条命令,gcc 是可执行文件,就像我们前面编译好的hello一样,是可以直接执行的,但是后面跟那么多参数是做什么的呢?其实简单来说就是根据不同的参数执行不同的命令,就像std(standard)参数一样,通过我们给出的不同参数执行不同标准下的编译标准。

那么这些参数是怎样传递进去的呢?这里就不得不提我们的main函数了,大家都知道main函数是整个程序的入口,程序的执行就是从这里开始,所以当然main函数就是我们的参数入口了,相信仔细阅读的你肯定在前面的图片中也有所发现了,这里我们再来看一下:

image

第一种就是不带参数的,第二种是带参数的,我们先上个简单代码:

#include 
/**
 *@author imliubo
 *@brief  main函数传参测试代码
 *@param  argc    传入的参数个数,argc>=1
 *@param  *argv[] 传入的参数的值,argv[0]=当前执行的程序名
**/
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;
}

我们先来看一下不加任何参数运行会打印什么:

image

可以看到我们不加任何参数时,argc跟argv的值都是有变化的,因为默认的第一个参数是当前程序的名(包含路径),参数总个数为1,我们再来看看我们加几个参数会打印什么:

image

可以看到我们在命令行中的四个参数都打印出来了,加上默认的一共5个一个也没有少,参数与参数之间是通过空格区分的(如果想将两个参数合并成一个可以通过加上引号实现),那么我们该怎样利用这些参数呢?一个好的程序是可以配置的,不同参数会有不同的运行结果,那么我们写一个通过参数控制打印不同字符的程序,这里我们用最简单的方法去实现:

#include 
#include 
/**
 *@author imliubo
 *@brief  main函数传参测试代码
 *@param  argc    传入的参数个数,argc>=1
 *@param  *argv[] 传入的参数的值,argv[0]=当前执行的程序名
**/
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

可以看出我们通过配置不同的参数,程序是不同的执行的结果,当然这是一种菜鸟程序员的写法,通过不断的比较去执行不同的代码,加入我们再在这个参数前面加上一个带有"-"修饰的字符,处理起来就要麻烦的多了,但是这种麻烦的问题肯定不只有我们才发现,所以已经有大神给我们写好工具了。

平常我们在处理很多繁杂的命令行参数时都会使用getopt()函数,比如像下面这条命令:

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

如果我们自己去解析这条命令中的参数,费时间不说,其中的逻辑还那么复杂,所以我们一般都是使用getopt()函数去解析,本篇文章就先到这,我会在下一篇文章中跟大家讨论一下。

文章难免有错误,如果有不对之处,还请及时指出我好及时改正。

欢迎关注我的专栏,我会不定期分享一些开发经验。

你可能感兴趣的:([C语言]你真的了解C语言吗之main函数(一))