欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

C语言12-main的参数与命令行传参、函数指针

程序员文章站 2022-05-12 08:42:18
...

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的程序调试,默认是没有参数的,但是可以在工程属性中设置:
C语言12-main的参数与命令行传参、函数指针
在命令行参数那一栏,可以填写参数,填写参数的格式约定,和命令行的一致(以空格作为分割)。
基于以上的知识,我们可以很容易的将我们的词法分析器,改写成命令行形式的,变得更灵活。

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;
}
相关标签: C语言入门开发