本期视频:点击这里
1、main函数的参数
在我们编写的简单程序中,main函数常常是不带参数的。比如这样一个简单的程序,给main函数添加参数似乎也没什么意义:
int main() { printf("Hello world!\n"); }
而实际上,main函数是可以添加参数的,而且在实际应用中也非常普遍。main函数完整的原型如下所示:
int main(int argc, char **argv);
主函数的参数的传入,不是在我们所编写的C程序代码中实现的,而是在运行C程序时,通过命令行传入。在程序运行的过程中,命令行的数据将会从main函数参数中传入程序代码中。其中,argc表示命令行参数的个数,argv以字符串数组的形式表示每一个命令行参数。需要注意的是这两个参数都包含可执行程序程序本身。比如如果我们编译出来的可执行程序名称为hello.exe,那么在命令行或批处理文件中写入以下命令:
hello.exe name1 name2
在这种情况下,argc为3,表示整个命令行有3个参数;argv表示保存每个命令行参数的数组,
argv[0]
、argv[1]、
argv[2]分别表示"hello.exe"、"name1"和"name2"。
main函数的参数在实际应用中比较常用。例如很多程序可能需要比较复杂的配置数据,这些配置数据通常写入一个配置文件中。在程序调用的时候,可以通过命令行参数传入配置文件的地址,在程序中通过命令行参数获取的地址来打开和解析配置文件。
2、指向函数的指针
在前面的内容中,我们讨论的指针变量指的是一种保存了数据内存地址的数据类型。通过指针变量保存的地址,我们可以获取目标内存中保存的数据。指针变量通常在函数参数传递、连续内存数据遍历等场合可以发挥重大作用。在定义时,需要根据所指向的内存中保存的数据类型来定义指针变量的类型。如我们需要一个执行int型数据的指针变量pInt,那么就应定义int *pInt。
事实上,指针变量不仅仅可以指向各种数据,还可以指向某个函数。在我们定义一个函数的时候,实际上函数名就代表了这个函数的入口地址,因此可以定义一种指向该类型函数的指针变量带指向函数的地址。
假设我们定义以下函数:
int GoodMorning(char *pName) { if(NULL == pName) { printf("Please enter the name.\n"); return 0; } else { printf("Good morning, %s.\n", pName); } return 1; }
众所周知,函数的调用格式为:函数名(实参列表)。在编译时,函数名实际上会被编译器转换为一个函数指针指向实际的地址。在程序中,函数名本身赋予指向函数的指针时就可以认为是函数的地址所在。我们定义一个指向函数的指针,并指向上述函数:
int (* greetings)( char * pName ) = GoodMorning ; //等价于int (* greetings)( char * pName ) = &GoodMorning ;
然后,我们可以通过函数指针调用该函数, 其功能和 GoodMorning ( "Jerry" )相同。
greetings ( "Jerry" );
需要注意的是,函数指针的类型必须与目标函数完全一致,包括返回值类型和参数个数、类型。如果出现不一致将无法指向。
3、函数指针作为参数传递
函数名以及指向函数的指针可以作为参数传递到另一个函数内。事实上,作为参数的函数指针与其他类型的变量指针并没有本质的区别,都是某一段内存的地址。在前面的内容中,由于指针类型是某种变量类型(比如int *),这段内存中的数据都将按照这种变量来处理;如果这个指针是某个函数类型,那么这段内存中的数据就被当做函数的代码,可以对其进行调用。
我们把上述程序稍作修改:
void greetings(int (* grt_func)( char * pName ), char * pName ) { grt_func(pName); }
像这类传入一个函数指针到其他函数,并在这个函数内部通过函数指针调用的方式称为回调函数。在多种实际开发的场合,回调函数都会起巨大作用。