当前位置: 首页 > news >正文

C语言编译和编译预处理

1.编译预处理

编译是指把高级语言编写的源程序翻译成计算机可识别的二进制程序(目标程序)的过程,它由编译程序完成。
编译预处理是指在编译之前所作的处理工作,它由编译预处理程序完成

在对一个源程序进行编译时,系统将自动调用预处理程序对源程序中的预处理部分作处理,处理完毕后自动编译源程序

2.宏定义

  • 宏:C语言源程序中允许用一个标识符来表示一个字符串
  • 宏名:被定义为宏的标识符
  • 宏展开:在编译预处理时,对程序中所有出现的宏名,都用宏定义中的字符串替换
  • 宏定义由宏定义命令完成
  • 宏展开由预处理程序自动完成
  • 宏分为:不带参数的宏和带参数的宏

3.不带参数的宏定义

不带参数的宏,其宏名后不带参数

              #define  标识符  字符串

             #define PI 3.1415926

            “标识符”是为所定义的宏名

           “字符串”为宏名将要被替换的字符串,可以是常量字符串、表达式字符串、格式字符串等 

宏定义

常量宏定义

//【例9. 1】常量的宏定义。
#include <stdio.h>
#define PI 3.1415926
void main()
{	float r,l,s,v;printf("Input radius:  ");scanf("%f",&r);l=2.0*PI*r;	//宏展开为  l=2.0*3.1415926*r;s=PI*r*r;	//宏展开为  s=3.1415926*r*r;v=4.0/3*PI*r*r*r; //宏展开为  v=4.0/3*3.1415926*r*r*r;printf("l=%.4f\ns=%.4f\nv=%.4f\n",l,s,v);
} 

表达式的宏定义

//【例9. 2】表达式的宏定义。
#include <stdio.h>
#define M (y*y+3*y)
void main()
{   int s,y;printf("Input a number:  ");scanf("%d",&y);s=3*M+4*M+5*M;  //宏展开为  s=3*(y*y+3*y)+4*(y*y+3*y)+5*(y*y+3*y);printf("s=%d\n",s);
}

函数名和格式字符串的宏定义

//【例9. 3】函数名和格式字符串的宏定义。
#include <stdio.h>
#define P printf
#define F "%4d\t%.2f\n"
void main()
{	int a=3, c=5, e=11;float b=4.6, d=7.9, f=22.08;P(F,a,b);  //宏展开为 printf("%4d\t%.2f\n",a,b);P(F,c,d);  //宏展开为 printf("%4d\t%.2f\n",c,d);P(F,e,f);  //宏展开为 printf("%4d\t%.2f\n",e,f);
}

说明:
1 )宏不是变量,不能存数据,也没有数据类型
2 )宏定义与变量定义不同,它只作字符串替换,不分配内存空间
3 )宏名习惯上用大写字母表示,以便与变量名相区别,但也允许用小写字母
4 )宏定义是用宏名来表示一个字符串,在宏展开时又以该字符串替换宏名,这只是一种简单的源程序代码的替换。
字符串中可以包含任何字符,预处理程序不作任何正确性检查。只有在编译已经预处理后的源程序时才能发现

                   define PI 3.l4l5926

        把数字“1”写成了小写字母“l”

5 宏定义不是语句或说明,在行末不必加分号 ,如加上分号则连分号也一起替换

            #define PI 3.1415926;

            ……

            area=PI*r*r;

           ……

       宏展开后为: area=3.1415926;*r*r;

         (6在源程序中用双引号引起来的字符串内,与宏名相同的字符不进行替换

//【例9. 4】双引号中与宏名相同的字符不作替换。
#include <stdio.h>
#define PI 3.1415926
void main()
{	printf("PI\n");	//不进行宏展开printf("%f\n",PI);  //进行宏展开
}
7 )宏定义允许嵌套,在宏定义的字符串中可以使用已经定义的宏名
#define PI 3.1415926
#define S PI*r*r          // PI 是已定义的宏名
语句: printf("%f",S);
宏展开后为: printf("%f",3.1415926*r*r);

       8)宏定义必须写在函数之外,其作用域为宏定义命令起到源程序结束。如要终止其作用域可使用# undef命令。

//【例9. 5】使用# undef结束宏的作用域。
#include <stdio.h>
#define PI 3.1415926
void main()
{    float r=2,area;area=PI*r*r;  
//宏展开为area=3.1415926*r*r; printf("area=%f",area);
}
#undef PI
f1()
{    float r=2,area;area=PI*r*r;  
//PI不能被宏展开,此处语法报错,PI没有定义printf("area=%f",area);
}

带参数的宏定义

C语言允许宏带参数。
宏定义中的参数称为形式参数,在程序中使用宏的语句中的参数称为实际参数。
在预编译时,带参数的宏不但要进行宏展开,而且要用实参去替换形参。
#define  宏名 ( 形参表 字符串
在源程序中使用带参数的宏的一般形式为 :
宏名 ( 实参表 )

//【例9. 6】带参数的宏定义。
#include <stdio.h>
#define MAX(a,b) (a>b)?a:b
void main()
{	int x,y,max;printf("Input two numbers:");scanf("%d,%d",&x,&y);max=MAX(x,y);  //宏展开为   max=(x>y)?x:y;printf("max=%d\n",max);
}
1 )宏名和形参表外的括号之间不能加空格。否则将空格以后的字符都作为替代字符串的一部分。
 例如 :
           宏定义“ #define  MAX(a,b)  (a>b)?a:b”
改写为:
#define  MAX  (a,b)  (a>b)?a:b

语句:           max=MAX(x,y);

宏展开后为:max=(a,b)  (a>b)?a:b(x,y); 是错误的。

2)宏定义中的形参是标识符,语句中的实参可以是表达式

【例9. 7】语句中的实参为表达式。
#include <stdio.h>
#define SQ(y) (y)*(y)
void main()
{    int a,sq;printf("Input a number:  ");scanf("%d",&a);sq=SQ(a+1);   //宏展开为 sq=(a+1)*(a+1);printf("sq=%d\n",sq);
}

3)在宏定义中,形参通常要用括号括起来以避免出错。

宏定义:

       #define SQ(y) (y)*(y)

(y)*(y)表达式的y都用括号括起来,因此结果是正确的

如果去掉括号,定义形式如下:

       #define SQ(y) y*y

语句:            sq=SQ(a+1);

宏展开后为:sq=a+1*a+1;

有时即使在参数两边加括号还是不够的

按以下形式定义:

            #define SQ(y) (y)*(y)

sq=1.0/SQ(a+1);

宏展开后为:

            sq=1.0/(a+1)*(a+1);    //先算除法,再算乘法

要想先算乘法后算除法,应该在宏定义中的整个字符串外加括号,按如下形式定义:

          #define SQ(y) ((y)*(y))

带参数的宏和函数的区别

带参数的宏和带参数的函数的区别:
1 )函数调用时,将值传给形参。带参数的宏展开时只是完整的实参表达式字符替代形参。
2 )函数调用在程序运行时进行。宏展开在编译前进行。
3 )函数中的形参和实参都要定义类型。宏的形参无需定义类型,它们都只是一串字符。
4 )调用函数只可得到一个返回值,而宏可以设法得到几个结果。
//【例9. 8】通过宏展开得到若干结果。
#include <stdio.h>
#define SSSV(L,W,H,SA,SB,SC,VV) SA=L*W;SB=L*H;SC=W*H;VV=W*L*H;
void main()
{   int l=3,w=4,h=5,sa,sb,sc,vv;SSSV(l,w,h,sa,sb,sc,vv);   //宏展开后为sa=l*w;sb=l*h;sc=w*h;vv=w*l*h;printf("sa=%d\nsb=%d\nsc=%d\nvv=%d\n",sa,sb,sc,vv);
} 

调用函数只可得到一个返回值,而宏可以设法得到几个结果。

文件包含

文件包含是指一个源文件中可以包含另一个源文件,即把另一个源文件插入到该文件中

文件包含命令的一般形式为:

#include "文件名"

#include <文件名>

文件包含命令的功能是把文件名所指定的文件插入到该命令行位置并取代该命令行,从而把指定的文件与当前的源程序文件连成一个源文件
•            #include"file2.c"
//【例9. 9】文件包含命令的使用。
(1)文件file1.c内容如下:
#include <stdio.h>
#include "file2.c"
void main()
{    int a,b,c;printf("Input two numbers: ");scanf("%d,%d",&a,&b);c=max(a,b);printf("max=%d\n",c);
}
//(2)文件file2.c内容如下:
int max(int x,int y)
{    int z;if(x>y)    z=x;else     z=y;return(z);
}
1 )文件头部的被包含文件,常以“ .h” 为后缀。也可用“ .c” 或“ .cpp” 为后缀,也可无后缀。
2 )一个 #include 命令只能指定一个被包含文件
3 )文件包含允许嵌套,即在一个被包含的文件中又可以包含另一个文件

4 )包含命令中的文件名可以用双引号括起来,也可以用尖括号括起来
#include <stdio.h>
         #include "file2.h "
使用尖括号时,系统直接在存放 C 库函数头文件的系统目录中寻找,这称为标准方式。
②使用双引号时,系统先在用户当前目录(即源文件所在目录)中寻找,若找不到,再按标准方式查找(即按尖括号的方式查找)

条件编译

条件编译:有时希望程序中一部分语句只在满足一定条件时才进行编译,不满足条件时不进行编译,或编译另一部分语句
利用条件编译,可以减少程序的输入,方便程序的调试,增强程序的可移植性

条件编译-形式一

#ifdef  标识符

  程序段1

#else

  程序段2

#endif

#ifdef  标识符

  程序段

#endif

功能是:如果所指定标识符在之前已被 #define命令定义过,则在编译时编译程序段1;否则编译程序段2

9. 10】给定半径r,求圆的面积s。要求设置条件编译:若π值已定义,则直接计算面积;若π值未定义,则定义π值后再计算面积。

#include <stdio.h>void main(){    float r,s;printf("Input radius:  ");scanf("%f",&r);#ifdef PI    //条件编译s=PI*r*r;              //程序段1#else#define PI 3.1415926   //程序段2s=PI*r*r;                #endifprintf("s=%f\n",s);}

1)程序中在条件编译前未定义π值(即PI),所以编译程序段2

2)若程序开头加入宏定义“#define PI 3.1415926”,则编译程序段1 

条件编译-形式二

#ifndef 标识符

  程序段1

#else

  程序段2

#endif

功能是:如果标识符之前未被#define命令定义过,则对程序段1进行编译,否则对程序段2进行编译

9. 11】按形式二修改9. 10】的程序。

#include <stdio.h>void main(){    float r,s;printf("Input radius:  ");scanf("%f",&r);#ifndef PI   //条件编译#define PI 3.1415926   //程序段1s=PI*r*r;                #elses=PI*r*r;              //程序段2#endifprintf("s=%f\n",s);}

1)程序中在条件编译前未定义π(即PI),所以编译程序段1

2)若在程序的开头加入宏定义“#define PI 3.1415926”,则编译程序段2

条件编译-形式三

#if 表达式

  程序段1

#else 

  程序段2

#endif

功能是:若表达式的值为真(非0),则编译程序段1,否则编译程序段2

9. 12】设置条件编译。求圆的面积或正方形的面积。

#include <stdio.h>#define PI 3.1415926#define R 1void main(){    float c,s;printf ("Input a number:  ");scanf("%f",&c);#if R        //条件编译s=PI*c*c;           //程序段1printf("Area of circle is : %f\n",s);#elses=c*c;                 //程序段2printf("Area of square is : %f\n",s);#endif}

程序宏定义中,定义R1,因此在条件编译时,表达式R的值为真,故编译程序段1,求圆的面积

说明

1 )三种形式的条件编译必须严格按照形式说明中的格式书写,每条条件编译命令必须单独成行
#if R s=PI*c*c;    // 出错
将程序段“ s=PI*c*c;” 与条件编译命令“ #if R” 写在同一行,是不正确的
2 )形式一和形式二中的标识符,若在条件编译之前被 #define 命令定义过,不管被定义为何值,甚至不定义任何值,只要被定义过,都会编译相应的程序段

#ifdef COMPUTER_A

  #define INTEGER_SIZE 16

#else

  #define INTEGER_SIZE 32

#endif

3 )形式三与形式一和形式二不同,“ #if” 后为表达式,不是标识符,所以不存在定义过还是未定义过的问题。只要该表达式的值为真(非 0 ),就编译程序段 1 ,否则编译程序段 2
4 )条件编译命令允许嵌套使用
在条件编译中也可以使用语句: #elif 它代表 else if

相关文章:

C语言编译和编译预处理

1.编译预处理 • 编译是指把高级语言编写的源程序翻译成计算机可识别的二进制程序&#xff08;目标程序&#xff09;的过程&#xff0c;它由编译程序完成。 • 编译预处理是指在编译之前所作的处理工作&#xff0c;它由编译预处理程序完成 在对一个源程序进行编译时&#xff0…...

ahb 总线的一些思考

1. 如何处理对不存在地址的访问&#xff1f; 当主设备试图访问内存映射中不存在的地址时&#xff0c;系统需要处理这一情况以避免错误或未定义行为。通常通过使用默认从设备或错误响应机制来管理。具体如下&#xff1a; 默认从设备&#xff1a;默认从设备响应对未定义或不存在…...

spark shuffle写操作——SortShuffleWriter

写入的简单流程&#xff1a; 1.生成ExternalSorter对象 2.将消息都是插入ExternalSorter对象中 3.获取到mapOutputWriter&#xff0c;将中间产生的临时文件合并到一个临时文件 4.生成最后的data文件和index文件 可以看到写入的重点类是ExternalSorter对象 ExternalSorter 基…...

ESP32CAM物联网教学12

ESP32CAM物联网教学12 MicroPython 视频服务 小智希望能在MicroPython中实现摄像头的视频服务&#xff0c;就像官方示例程序CameraWebServer那样。 下载视频服务驱动库 小智通过上网搜索&#xff0c;发现相关的教学材料还不少&#xff0c;并且知道有人已经写出了视频服务的驱…...

【C++精华铺】12.STL list模拟实现

1.序言 STL (Standard Template Library)是C标准库中的一个重要组件&#xff0c;提供了许多通用的数据结构和算法。其中&#xff0c;STL list是一种带头双向链表容器&#xff0c;可以存储任意类型的元素。 list的特点包括&#xff1a; 双向性&#xff1a;list中的元素可以根据需…...

ChatGPT Mac App 发布!

2024 年 6 月&#xff0c;OpenAI 的大语言模型 ChatGPT 的 Mac 客户端与 ChatGPT-4o 一起发布了。ChatGPT Mac 户端可以让用户直接在 Mac 电脑上使用 ChatGPT 进行对话。它提供了一个简单易用的用户界面&#xff0c;用户可以在其中输入文本或语音指令&#xff0c;并接收模型生成…...

ACE之ACE_Time_Value

简介 ACE_Time_Value在ACE中表示时间&#xff0c;集成不同平台的时间 结构 #mermaid-svg-dGoKn1R7GicabUif {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-dGoKn1R7GicabUif .error-icon{fill:#552222;}#mermaid-…...

[论文笔记] 自对齐指令反翻译:SELF-ALIGNMENT WITH INSTRUCTION BACKTRANSLATION

https://arxiv.org/pdf/2308.06259 这篇论文介绍了一种名为“指令反向翻译”(instruction backtranslation)的方法,用于通过自动标记人类书写的文本和相应的指令来构建高质量的指令跟随语言模型。这里是一个通俗易懂的解释: 一、背景 通常,训练一个高质量的指令跟随语言…...

算术运算符. 二

# 表达式 # 操作数和运算符组成 比如 11 # 作用&#xff1a;表达式可以求值&#xff0c;也可以给变量赋值。 # Python算术运算符&#xff1a; # - * / % //&#xff08;整除:向下取整&#xff09; ** print(10 4) # 14 print(10 - 4) # 6 print(10 * 4) # 40 …...

代码优化方法记录

每次代码 review 之后&#xff0c;对 review 的情况进行总结记录&#xff0c;产出实际经验&#xff0c;方便组内学习、分享。 1、提取公共内容 公共内容要提取&#xff0c;避免重复编写&#xff1b; 2、css 色值使用变量 css 中的色值、字体&#xff0c;都换成组件库中的变…...

qt 图形、图像、3D相关知识

1.qt 支持3d吗 Qt确实支持3D图形渲染。Qt 3D模块是Qt的一个组成部分&#xff0c;它允许开发者在Qt应用程序中集成3D内容。Qt 3D模块提供了一组类和函数&#xff0c;用于创建和渲染3D场景、处理3D对象、应用光照和纹理等。 Qt 3D模块包括以下几个主要组件&#xff1a; Qt 3D …...

【逆向基础】十、工具分享之DIE(Detect It Easy)

一、简介 DIE&#xff08;Detect It Easy&#xff09;是一款可以轻松检测PE文件的程序&#xff1b;其主要作用是查壳&#xff0c;并将pe文件的内容解析出来&#xff0c;包括PE文件中包含的导入函数、导出函数的名称及地址&#xff0c;入口函数地址等&#xff0c;是技术人员分析…...

Netcat:——网络瑞士军刀

Netcat: 网络瑞士军刀 概述 Netcat&#xff08;通常称为 nc&#xff09;是一个功能强大的网络工具&#xff0c;广泛用于网络测试和调试。它能够读取和写入网络数据&#xff0c;支持TCP、UDP协议&#xff0c;可以用于端口扫描、端口监听、文件传输等多种用途。 主要用途 获取…...

C++ //练习 14.50 在初始化ex1和ex2的过程中,可能用到哪些类类型的转换序列呢?说明初始化是否正确并解释原因。

C Primer&#xff08;第5版&#xff09; 练习 14.50 练习 14.50 在初始化ex1和ex2的过程中&#xff0c;可能用到哪些类类型的转换序列呢&#xff1f;说明初始化是否正确并解释原因。 struct LongDouble{LongDouble(double 0.0);operator double();operator float(); }; Long…...

【开源 Mac 工具推荐之 1】gibMacOS:方便快捷的 macOS 完整包下载 Shell 工具

简介 gibMacOS 是由 GitHub 开发者 corpnewt 编写的一款 Shell 工具。它采用 Python 编程语言&#xff0c;可以让用户打开后在纯文本页面中轻松选择并下载来源于 Apple 官方的 macOS 完整安装包。 Repo 地址&#xff1a;https://github.com/corpnewt/gibMacOS &#xff08;其…...

pdf文件如何快速英文转中文?

要将 PDF 文件中的英文内容转换为中文&#xff0c;你可以使用以下几种方法&#xff1a; 1、在线翻译工具&#xff1a; 使用网上的免费在线翻译工具&#xff0c;如Google翻译、百度翻译或有道翻译&#xff0c;将整个 PDF 文档粘贴到工具中进行翻译。 2、专业翻译软件&#xf…...

程序的控制结构——if-else语句(双分支结构)【互三互三】

目录 &#x1f341; 引言 &#x1f341;if-else语句&#xff08;双分支结构&#xff09; &#x1f449;格式1&#xff1a; &#x1f449;功能&#xff1a; &#x1f449;程序设计风格提示&#xff1a; &#x1f449;例题 &#x1f449;格式2&#xff1a; &#x1f449;…...

[C++]初识C++(命名空间,命名空间使用,函数重载,缺省参数等)

&#x1f496;&#x1f496;&#x1f496;欢迎来到我的博客&#xff0c;我是anmory&#x1f496;&#x1f496;&#x1f496; 又和大家见面了 欢迎来到C探索系列 作为一个程序员你不能不掌握的知识 先来自我推荐一波 个人网站欢迎访问以及捐款 推荐阅读 如何低成本搭建个人网站…...

每天一个数据分析题(四百十六)- 线性回归模型

根据模型假设&#xff0c;线性回归模型中误差项的方差为 A. 常数 B. 函数 C. 随机变量 D. 以上都不是 数据分析认证考试介绍&#xff1a;点击进入 题目来源于CDA模拟题库 点击此处获取答案 数据分析专项练习题库 内容涵盖Python&#xff0c;SQL&#xff0c;统计学&#…...

JupyterNotebook中导出当前环境,并存储为requirements.txt

​使用Anaconda管理Python环境时&#xff0c;可以轻松地导出环境配置&#xff0c;以便在其他机器或环境中重新创建相同的环境。可以通过生成一个environment.yml文件实现的&#xff0c;该文件包含了环境中安装的所有包及其版本。但是&#xff0c;常常在一些课程中JupyterNotebo…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)

HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...

可靠性+灵活性:电力载波技术在楼宇自控中的核心价值

可靠性灵活性&#xff1a;电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中&#xff0c;电力载波技术&#xff08;PLC&#xff09;凭借其独特的优势&#xff0c;正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据&#xff0c;无需额外布…...

多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验

一、多模态商品数据接口的技术架构 &#xff08;一&#xff09;多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如&#xff0c;当用户上传一张“蓝色连衣裙”的图片时&#xff0c;接口可自动提取图像中的颜色&#xff08;RGB值&…...

Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)

引言&#xff1a;为什么 Eureka 依然是存量系统的核心&#xff1f; 尽管 Nacos 等新注册中心崛起&#xff0c;但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制&#xff0c;是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...

css3笔记 (1) 自用

outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size&#xff1a;0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格&#xff…...

中医有效性探讨

文章目录 西医是如何发展到以生物化学为药理基础的现代医学&#xff1f;传统医学奠基期&#xff08;远古 - 17 世纪&#xff09;近代医学转型期&#xff08;17 世纪 - 19 世纪末&#xff09;​现代医学成熟期&#xff08;20世纪至今&#xff09; 中医的源远流长和一脉相承远古至…...

Go 语言并发编程基础:无缓冲与有缓冲通道

在上一章节中&#xff0c;我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道&#xff0c;它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好&#xff0…...

手机平板能效生态设计指令EU 2023/1670标准解读

手机平板能效生态设计指令EU 2023/1670标准解读 以下是针对欧盟《手机和平板电脑生态设计法规》(EU) 2023/1670 的核心解读&#xff0c;综合法规核心要求、最新修正及企业合规要点&#xff1a; 一、法规背景与目标 生效与强制时间 发布于2023年8月31日&#xff08;OJ公报&…...

WPF八大法则:告别模态窗口卡顿

⚙️ 核心问题&#xff1a;阻塞式模态窗口的缺陷 原始代码中ShowDialog()会阻塞UI线程&#xff0c;导致后续逻辑无法执行&#xff1a; var result modalWindow.ShowDialog(); // 线程阻塞 ProcessResult(result); // 必须等待窗口关闭根本问题&#xff1a…...

MyBatis中关于缓存的理解

MyBatis缓存 MyBatis系统当中默认定义两级缓存&#xff1a;一级缓存、二级缓存 默认情况下&#xff0c;只有一级缓存开启&#xff08;sqlSession级别的缓存&#xff09;二级缓存需要手动开启配置&#xff0c;需要局域namespace级别的缓存 一级缓存&#xff08;本地缓存&#…...