C语言12-main的参数与命令行传参、函数指针
文章目录
main函数的参数与命令行传参
C标准中,main函数的函数原型:
int main(int argc, char* argv[]);
而main作为C语言中第一个被调用的,并且C语言中最后返回的函数,貌似是没有办法与其它函数交互的。
那么main为什么还要设计返回值和参数。
一切的答案在:命令行传参。
main确实是C语言中的第一个函数,但是,从操作系统角度而言,main也是被操作系统去调用的。
操作系统为了能够和程序(命令)交互,对命令行传递参数做了对应的约定。使得我们可以通过命令行,传递参数给C程序。
不如回顾命令行命令:
cd c:\
以上的cd,就是一个C程序, 而命令行中,C程序之后,用空格分隔的字符串,就是参数。一个程序运行,可以有多个参数。
main的两个参数:
- argc:参数的数目,大于等于1
- argv:是一个指针数组,里面存储了多个字符串,其实就是各个参数,最后一个元素的值NULL。
int main(int argc, char* argv[])
{
printf("argc:%d\r\n", argc);
for (size_t i = 0; argv[i]!=NULL; i++)
{
printf("%d:%s\r\n", i, argv[i]);
}
return 0;
}
VS中带参数调试
VS的程序调试,默认是没有参数的,但是可以在工程属性中设置:
在命令行参数那一栏,可以填写参数,填写参数的格式约定,和命令行的一致(以空格作为分割)。
基于以上的知识,我们可以很容易的将我们的词法分析器,改写成命令行形式的,变得更灵活。
int main(int argc, char* argv[])
{
TokenParse(argv[1]);
return 0;
}
函数指针(可以增加灵活性)
函数名可以打印:
int main(int argc, char* argv[])
{
printf("%p\r\n", main);
return 0;
}
事实上,函数名其实就是该函数在内存中的首地址
所以可以用指针变量保存函数名———函数指针
区分返回指针的函数,与函数指针
以下,定义了一个可以返回指针的函数(而不是函数指针)。
int* Fun(int x, int y);
如何定义一个函数指针?
int (*g_pfnAdd)(int x, int y);
int main(int argc, char* argv[])
{
g_pfnAdd = &MyAdd;
return 0;
}
可以通过函数指针去调用函数。
int main(int argc, char* argv[])
{
g_pfnAdd = &MyAdd;
printf("%d\r\n", g_pfnAdd(10, 20));//第一种
printf("%d\r\n", (*g_pfnAdd)(10, 20));//第二种,更严谨
return 0;
}
函数指针有什么用?
学习语言三个层次:语法、原理层次、设计层次。
函数指针的应用,大都是设计层次(高内聚、低耦合)的东西,简单而言,它降低了一个产品中各方的耦合性。
为统一中断产生与实际的业务逻辑,因此产生函数。
而回调函数,就是通过函数指针实现的机制。
void*指针
在C语言中,还有一种**没有解释方式的指针 **
void*
void*指针的用处
因为void*指针光有地址,没有解释方式,所以不能用来解引用。它的特点是:
- 任意类型的地址,都可以无警告的转变为void*类型的地址
- void*类型的地址,不能直接赋值给其它的指针
在C语言中,利用这个特性,写出一些“万能类型”的函数。 比如:
void *memset( void *dest, int c, size_t count ); //将dest地址处的count个字节的空间,全部设置为c值。
预编译指令-宏
所谓的预编译指令,就是指在“编译C语言代码”之前,对源码进行处理的指令。
预编译指令,不是C语言。
C语言中的预编译指令,都以#开头。
实际上,编译器的编译过程,是:
- 预处理:根据预编译指令,得到最终的C源码
- 编译I:将C源码变为汇编代码
- 编译II:将汇编代码编译为机器码
比如,cl.exe展示:
cl /c /P first.c
cl /c /Fas first.c
宏将是一个预编译指令。宏的本质是查找替换
无参宏
#define MYMSG "hello, world"
有参宏
#define MYADD(a,b) a+b
#define MYMUL(a,b) a*b
int main(int argc, char* argv[])
{
printf("%d\r\n", MYMUL(5‐4, 6‐2)/* 5‐4*6‐2 */);
return 0;
}
因为宏是直接查找替换,所以使用不当,容易造成问题。所以建议是,多打括号。
#define max(a,b) (((a) > (b)) ? (a) : (b))
宏中字符串化与token化
有参宏的参数,若有一个#,那么会将参数字符串化
#define MYMACRO(a) "hello: "#a
int main(int argc, char* argv[])
{
printf(MYMACRO(hahahaha));
return 0;
}
比如,以上的宏被替换后为:
"hello: ""hahahaha"
token化
以下利用宏,快速的声明以下一系列由规律的函数:
int MyAddint(int x, int y);
double MyAdddouble(double x, double y);
期待有个宏:
DeclareMyFun(type);
main()
{
DeclareMyFun(int);//int MyAddint(int x, int y);
DeclareMyFun(double);//double MyAdddouble(double x, double y);
}
可能做了如下尝试:
#define DeclareMyFun(type) type MyAddtype(type x, type y)
DeclareMyFun(int);
DeclareMyFun(double);
但是,这是错的,他们展开后,长这样:
int MyAddtype(int x, int y);
double MyAddtype(double x, double y);
如果需要在一个token特别强调它的某部分,其实是宏参数,就需要使用##。
#define DeclareMyFun(type) type MyAdd##type(type x, type y)
DeclareMyFun(int);
DeclareMyFun(double);
int main(int argc, char* argv[])
{
return 0;
}