简介
程序执行总是从main函数开始,如果有有其他函数,则完成对其他函数的调用后再返回到主函数,最后由main函数结束整个程序。在执行程序时,由系统调用main函数。main 函数是在程序启动中完成对具有静态存储期的非局部对象的初始化之后被调用的。它是程序在有宿主 (hosted)环境(亦即有操作系统)中所指定的入口点。自立程序(启动加载器,操作系统内核,等等)的入口点则是由实现定义的。主函数的两个形参形式中的形参,允许从执行环境中传递任意的多字节字符串(它们通常被称为命令行参数),各个指针 argv.. argv[argc-1] 指向每个这些字符串的第一个字符。argv是指向一个表示用于执行该程序自身的名字的空结尾多字节字符串(或者当执行环境不支持时,为空字符串 "")的开头字符的指针。这些字符串是可以改动的,虽然对它们的改动并不会被传回给执行环境:比如可以用 std::strtok 来使用它们。由 argv 所指向的数组的大小至少为 argc+1,其最后一个元素 argv[argc] 保证为一个空指针。
参数
模块化程序设计方法是把整个程序划分为若干功能较为单一的程序模块,然后分别予以实现,最后再把所有的程序模块像搭积木一样装配起来。利用函数可以实现程序的模块化,可以大大地减轻编码工作量。在函数内部或复合语句块内部定义的 auto 类型变量称为局部变量。局部变量的作用域从该局部变量定义的位置开始到本函数或复合语句结束处。局部变量的生存期较短暂。当局部变量所在的函数被调用时,局部变量内存空间被分配,函数调用结束后,局部变量内存空间被释放。函数的形参和实参均属于局部变量,调用函数时,形参内存空间被分配。函数调用结束后,形参内存空间被释放。C语言函数参数传递可分为三种情况:简单变量作为函数参数、数组名作为函数参数以及指针作为函数参数。C程序最大的特点就是所有的程序都是用函数来装配的。main()称之为主函数,是所有程序运行的入口。其余函数分为有参或无参两种,均由main()函数或其它一般函数调用,若调用的是有参函数,则参数在调用时传递。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | int main(void)//整数类型 主函数(无类型) { ... y1=f1(x1,x2);//赋值过程 ... } f1(int a,int b)//f1(整数类型 a,整数类型 b) { //百度百科示例代码..... Y2=f2(x3,x4);//赋值过程 //百度百科示例代码 } f2(int m,int n)//f2(整数类型 m,整数类型 n) { //百度百科示例代码 } |
在前面课程的学习中,对main()函数始终作为主调函数处理,也就是说,允许main()调用其它函数并传递参数。事实上,main()函数既可以是无参函数,也可以是有参的函数。对于有参的形式来说,就需要向其传递参数。
我们先看一下main()函数的带参的形式:
1 2 3 4 | int main(int argc,char **argv)//整数类型 主函数(整数类型 统计参数个数,字符类型 **指针至字符) { //百度百科示例代码...... } |
从函数参数的形式上看,包含一个整型和一个指向字符指针的指针。当一个C的源程序经过编译、链接后,会生成扩展名为.EXE的可执行文件(对windows操作系统而言),这是可以在操作系统下直接运行的文件,换句话说,就是由系统来启动运行的。由系统在启动运行时传递参数。在操作系统环境下,一条完整的运行命令应包括两部分:命令与相应的参数。其格式为:
命令参数1参数2....参数nt;此格式也称为命令行。命令行中的命令就是可执行文件的文件名,其后所跟参数需用空格分隔,并为对命令的进一步补充,也即是传递给main()函数的参数。
在操作系统环境下,一条完整的运行命令应包括两部分:命令与相应的参数。其格式为:
命令参数1参数2....参数nt;
此格式也称为命令行。命令行中的命令就是可执行文件的文件名,其后所跟参数需用空格分隔,并为对命令的进一步补充,也即是传递给main()函数的参数。
命令行与main()函数的参数存在如下的关系:
设命令行为:program str1 str2 str3 str4 str5
其中program为文件名,也就是一个由program.c经编译、链接后生成的可执行文件program.exe,其后各跟5个参数。对main()函数来说,它的参数argc记录了命令行中命令与参数的个数,共6个,指针数组的大小由参数argc的值决定,即为char*argv[6],指针数组的取值情况如图6-15所示。
数组的各指针分别指向一个字符串。应当引起注意的是接收到的指针数组的各指针是从命令行的开始接收的,首先接收到的是命令,其后才是参数。
下面用实例来说明带参数的main()函数的正确使用。

图6-15
[例6-29] 利用图形库函数绘制一个变化的环。它是把一个半径为R 1的圆周分成n份,然后以每个等分点为圆心,以R s为半径画n个圆(关于作图的详细理论本教材第9章第1节作了专门介绍,这里只作简单分析)。利用main()函数的带参数形式,我们可以从键盘以命令行的方式输入R 1和R s及屏幕的背景色。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | #include <graphics.h>//包含图形库函数的头文件 #include <math.h> #define pi 3.1415926 intmain(int argc,char **argv) { int x,y,r1,rs,color; double a; int gdriver=DETECT,gmode; initgraph(&gdriver,&gmode,"..\\bgi")//启动图形工作方式 r1=atoi(argv[1]);//计算基础圆半径 rs=atoi(argv[2]);//计算同心圆半径 color=atoi(argv[3]);//背景色 cleardevice();//清除图形屏幕 setbkcolor(color);//设置背景色 setcolor⑷;//设置图形显示颜色 for(a=0;a<=2*pi;a+=pi/18)//绘制同心圆 { x=r1*cos(a)+320; y=r1*sin(a)+240; circle(x,y,rs);//以圆心坐标为x、y,半径为rs画圆 } getch();//等待按键继续 closegraph();//关闭图形工作方式 return 0; } |
若程序名为L6-29.c,经编译、连结生成可执行文件L6-29.exe。在操作系统的环境下运行程序,命令行方式为:
具体步骤:
1、Win + R打开运行;
2、输入cmd打开命令行;
3、进入到要执行的exe文件所在目录,如要要执行E:\程序设计\Ctest.exe文件,则输入E:回车(注意:如果要更改其他盘目录必须首先进入要进入盘的根目录,例如进入E盘则输入E:不用输入cd E:)进入E盘根目录,再输入cd E:\程序设计 则进入了当前目录,最后就可以输入参数了,例如Ctest yishi weban回车即可(Ctest为要执行的可执行文件,后面两个也为要传递给main函数的参数);
l6-29 40 20 3
则命令行与main()函数的参数有如图6-16所示的关系
图6-16中,argv是程序名,argv是r1的值,
argv是rs的值,argv是屏幕的背景色。
由于指针数组均存放字符串,所需的圆半径及背景
色彩通过atoi()函数转换为整型。
通过带参数的main()函数,我们可以为自己的程序设置口令,在运行程序的命令行中给出所需的口令,正确则继续,否则退出。程序图形输出如图6-17所示。
[例6-30]将上述程序作修改,在程序的入口处添置密码,若给定密码正确,则显示图形。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | #include<graphics.h> #include<math.h> #definepi3.1415926 int main(int argc, char **argv) { intx,y,r1,rs,color; doublea; intgdriver=DETECT,gmode; if(strcmp(argv[1],"pass")!=0)//设置口令的比较 {printf("passworderror!\n"); exit(0); } initgraph(&gdriver,&gmode,"..\\bgi"); r1=atoi(argv[2]); rs=atoi(argv[3]); color=atoi(argv[4]); cleardevice(); setbkcolor(color); setcolor⑷; for(a=0;a<=2*pi;a+=pi/18) { x=r1*cos(a)+320; y=r1*sin(a)+240; circle(x,y,rs); } getch(); closegraph(); return0; } |
在操作系统的环境下运行程序, 命令行中增加口令“pass”,命令行方式为:
l6-30 pass 20 40 3
指针数组的存储字符串如图6-18所示。
若给定字符串argv的值是pass,则程序正确运行,否则程序退出。口令正确的情况下,显示的图形为图6-17中的一个。

6-18
函数分类
从用户使用的角度看,函数有两种:
1、标准函数,即库函数。这是由系统提供的。
2、用户自定义的函数。可以解决用户的专门需要。
二、从函数的形式看,函数分两类:
1、无参函数
printstar和print_message就是无参函数。在调用无参函数时,主调函数并不将数据传送给被调用函数,一般用来执行指定的一组操作。
2、有参函数
在调用函数时,在主调函数和被调用函数之间有数据传递。也就是说,主调函数可以将数据传递给被调用函数使用,被调用函数中的数据也可以带回来供主调函数使用。
一般形式
无参函数
类型标示符 函数名()
{
函数体
}
有参函数定义
类型标示符 函数名 (形式参数列表)
{
函数体
}
可以有空函数
它的形式为
类型说明符 函数名()
{ }
例如:
dummy(){}
传统方式
在老版本c语言中,对形参类型的声明是放在函数定义的第二行,也就是不在第一行的括号内指定形参的类型,而在括号外单独指定,例如上面定义的max函数可以写成一下形式:
1 2 3 4 5 6 | int max(x,y)/*整数类型max函数;并指定形式参数x,y*/ int x,y;/*整数类型x,y;形式参数xy是整数类型*/ {int z;/*整数类型z*/ z=x>y?x:y; return(z); } |
返回值
在 C89 标准中,main( ) 是可以接受的。Brian W. Kernighan 和 Dennis M. Ritchie 的经典巨著 The C programming Language 用的就是 main( )。不过在最新的 C99 标准中,只有以下两种定义方式是正确的:
1 | int main(void){/*百度百科示例代码*/}//整数类型主函数(无类型) |
1 | int main(int argc,char **argv)//整数类型主函数(整数类型 统计参数个数,字符类型*数组指针至字符[]) |
其中char **argv可以写成char *argv,两者等价main 函数的返回值类型必须是 int ,这样返回值才能传递给程序的激活者(如操作系统)。
如果 main 函数的最后没有写 return 语句的话,C99 规定编译器要自动在生成的目标文件中(如 exe 文件)加入return 0; ,表示程序正常退出。不过,我还是建议你最好在main函数的最后加上return 语句,虽然没有这个必要,但这是一个好的习惯。也就是说在最新的C语言标准中强制要求main函数的返回值类型为int,main函数的返回值是传递给操作系统,让操作系统判断程序的执行情况(是正常结束还是出现异常)
误区
很多人甚至市面上的一些书籍,都使用了void main() ,其实这是错误的。C/C++中从来没有定义过void main()。C++之父Bjarne Stroustrup在他的主页上的FAQ中明确地写着The definition void main() {/*...*/} is not and never has been C++, nor has it even been C.(void main() 从来就不存在于C++或者C)。在C和C++中,不接收任何参数也不返回任何信息的函数原型为“void foo(void);”。可能正是因为这个,所以很多人都误认为如果不需要程序返回值时可以把main函数定义成void main(void) 或void main()。然而这是错误的!main函数的返回值应该定义为int类型,C和C++标准中都是这样规定的。虽然在一些编译器中,void main可以通过编译(如vc6),但并非所有编译器都支持void main,因为标准中从来没有定义过void main。g++3.2、dev C++等环境中如果main函数的返回值不是int类型,根本无法通过编译。而gcc3.2则会发出警告。所以,如果你想你的程序拥有很好的可移植性,请一定要用int main。
应用
C++中的main函数
C++继承了C语言的大部分特性,因此保留了“程序总是从main函数开始执行,且总是默认从main函数的return语句或结尾处结束运行”这一传统,但是要注意,C++中的main函数要想作为程序执行的出入口,必须写在全局(Global)范围,不能写成某个结构体或某个类的成员。虽然main函数可以作为结构体或者类的成员函数,但相应地会失去作为程序出入口的功能。C++中全局main函数的书写格式与C语言完全相同,功能也完全相同,且同一C++程序同样只能有一个全局main函数。
Java中的main函数
Java同样是以main函数作为程序执行出入口的,但Java作为“更纯洁”的面向对象语言,它的main函数与C/C++有很大的不同。
首先,返回值的概念淡化,在Java Application中main不允许返回值,因此int main是被禁止的,必须使用void main,int main仅限在JavaBean中使用。
其次,Java中所有的函数必须属于类,没有什么全局函数一说,因此main函数不能是全局成员,必须是某个类的成员。
第三,由于main函数变成了类的成员函数,因此要想直接被系统调用,还必须使用public static使其成为静态函数并具有公开权限。
第四,main函数的参数被简化,只需要提供字符串数组即可,不需要提供参数个数(这是由于Java的数组具有下标检查功能的原因)
Java Application中的main函数一般格式如下(类名可以自定义,但保存为Java源码时,主文件名必须与类名相同,否则可能无法运行)
1 2 3 4 5 | public class MainDemo { public static void main(String[] args){ //TODO:在此处写入主函数的内容 } } |
Java Applet的运行机制与Java Application完全不同,因此不需要main函数
C#中的main函数
C#中的main函数与Java大同小异,同样必须是类成员,同样使用字符串数组作唯 一参数,同样是静态函数,同样的void main,与之不同的是:main的首字母变成了大写,即"Main函数“,且是否限定为public级别已经无所谓了(默认没有public,但某些场合可能还是需要public)另外,需要注意的是,C#中不再有”类名必须与主文件名同名“的限制,即使类名不和主文件名相同,程序照样可以运行。C#应用程序中的main函数默认是这样的(注意main的首字母已是大写)。C#和C/C++、java不同的是C#不在拘泥于必须从main()函数开始执行,C#是属于事件触发。
1 2 3 4 5 | class Program { static void Main(string[] args){ //TODO:在此处写入主函数的内容 } } |