预处理和宏定义【c语言】

预处理和宏定义【c语言】,第1张

        任何一个ANSI C,都有两个环境,一个翻译环境一个运行环境。


        运行环境相当于windows,翻译环境相当于编译器。




一、程序的编译和预处理:

        一个程序从代码到执行程序,要进行一系列复杂的工作,在vs2022的编译器中,这些工作都我们完成了。


都这里只简单讨论其执行步骤(如果想深入了解推荐《程序员的自我修养》)。


     

        vs2022是集成开发环境(IDE),其内部集成和编译器(cl.exe)和连接器(link.exe),来帮助使用者快速和方便的编译代码,最后生成可执行程序.exe。


这里到生成可执行程序的步骤,可以统称为编译。


        但是在某些编译器上则需要自己手动进行编译和处理。


编译环境:

        这里把一个代码文件test.c生成了一个可执行test.exe  

 步骤:

1. 预处理(预编译)  生成中间件 test.i

这里会发现,中间件因为加载了头文件,代码行数到了六万多行。


还删除了注释。


并且把#define定义的符号进行了替换。


2.编译结果保存在test.s中。


中间件test.s中保存的就是源代码的汇编代码。


3.汇编完成,结果保存在test.obj中。


是一堆二进制指令和形成符号表。


4.最后进行链接,进行到这里的时候编译器就能判断外部函数是否正确的被调用。


比对符号表,发现符号表上并没有找的对应的函数就会报错。


 

运行环境:  

1.一个程序要执行,必须载入内存。


在windows下,是系统帮我们载入的,在其他平台上需要自己手动烧录。


2.程序的开始main()函数。


3.开始执行程序代码。


4.终止程序。



二、预处理

        #include 和 #define都是预处理指令,一个是用来加载头文件,一个是用来宏定义。


        在vs编译器内部有很多的宏定义

  • __FILE__           当前文件
  • __LINE__          当前代码行
  • __TIME__          文件被编译的时间
  • __DATE__         文件被编译的日期
  • __func__           当前函数名
  • __STDC__        编译器遵循ANSI C,其值为1

         

        当然我们也可以自己定义一个宏,这时就需要使用预处理命令#define。


         这里定义了一个MAX(x,y),会发现相较于写一个比较大小的函数,这种写法简洁方便。


但是要注意,#define并不是如同函数那样接收参数,而是单纯的符号替换。


        第二个文件为程序编译时,产生的中间件。


这里会发现这里#define定义的宏,完全是替换的功能


 并不像函数那样在其内部进行计算,所以没有在栈区产生消耗,所以速度相较于函数来说比较快一些。


        但是因为其是单纯的符号替换,所以这里必需要注意符号的优先级

         

        比如这里只是想计算10乘以比较大小后的数,但是这里进行替换后却发现10*10是先计算的,会造成错误。


所以这里要注意必需要加上括号。


 

         但是如果只对单个的参数括了起来,还是会出现错误,所以这里需要把整体一起括起来

        而且,#define定义的宏,输入的参数不能带副作用(a+1没有副作用,但是a++有副作用,其改变了a本身的值)

#define MAX(x,y) ((x)>(y)?(x):(y))
int a=5;
int b=8;
int c=MAX(a++,b++);

        

        这里其实就会发现,前面变量计算后,影响到了后面的值。


所求的值跟原来进行比较的值已经不一样了。


        

        对应一个函数来说其参数据有固定的类型,要进行不同类型的比较,要定义多个函数,但是宏没有类型检查,可以接受任意类型的数据。


        宏没办法像函数一样进行调试,宏也不能递归。


        所以要进行宏定义的时候,要根据自己需要来选择要不要使用。


 

#undef:移除宏定义

#define NUM 10;
#undef NUM

三、条件编译:

#if 常量表达式
//...
​#endif
#if 常量表达式
//...
#elif 常量表达式 
//...
#else 常量表达式 
//...
#endif
#define NUM 1
#if NUM==1 
	printf("1\n");
#elif NUM==2
	printf("2\n");
#else
	printf("3\n");
#endif

        这段代码的含义是,如果NUM等于1,则打印1,如果NUM等于2,打印2,否则打印3。


会发现跟 if() 语句非常相似。


#if defined(MAX)
//...
#endif        //等价于下面
                
#ifdef MAX
//...
#endif

        这段代码的含义是,如果MAX被宏定义,则执行后面的代码。


#if !defined(MAX) 
//...
#endif    //等价于下面

#ifndef MAX
//...
#endif

        这段代码的含义是,如果MAX没有被宏定义,则执行后面的代码。


#if defined(OS_UNIX)

     #ifdef OPTION1
         unix_version_option1();
     #endif

     #ifdef OPTION2
         unix_version_option2();
     #endif

#elif defined(OS_MSDOS)

     #ifdef OPTION2
         msdos_version_option2();
     #endif

#endif

        条件编译指令可以嵌套使用,但是会发现代码可读性非常差,很难以理解。


PS:进入vs自带的库函数会发现,条件编译在vs底层非常常见。


避免头文件重复引用:
1.#pragma once

        #pragma也是一个预处理指令,这里加一个once,一般写在头文件最上面,其意思是只加载一次头文件。


但是这种写法在其他编译器上有可能不支持。


所以还有一种普遍的写法。


#ifndef _TEST_H 
#define _TEST_H
    #includ
    //...
#endif

          这种写法也可以防止多次加载头文件,并且兼容性好。


欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/584907.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-12
下一篇 2022-04-12

发表评论

登录后才能评论

评论列表(0条)

保存