C语言编译和编译预处理

编译预处理

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

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

宏定义

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

不带参数的宏定义

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

        #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   //程序段2

        s=PI*r*r;                

    #endif

    printf("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   //程序段1

        s=PI*r*r;                

    #else

        s=PI*r*r;              //程序段2

    #endif

    printf("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 1

void main()

{    float c,s;

    printf ("Input a number:  ");

    scanf("%f",&c);

    #if R        //条件编译

        s=PI*c*c;           //程序段1

        printf("Area of circle is : %f\n",s);

    #else

        s=c*c;                 //程序段2

        printf("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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/40568.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

全国青少年软件编程等级考试-四级-奇偶之和(真题)

题目&#xff1a;奇偶之和 1.准备工作 (1)保留舞台中的小猫角色&#xff1b; 2.功能实现 (1)分别计算1&#xff5e;100中&#xff0c;奇数之和&#xff0c;偶数之和&#xff1b; (2)说出奇数之和&#xff0c;偶数之和。 讲解&#xff1a; 1、如何判断奇偶数 奇数是指除以2有…

C++deque容器

文章目录 deque容器概念deque操作deque对象的带参数构造deque头部和末尾的添加移除操作deque的数据存取deque与迭代器deque赋值deque插入deque删除 deque容器概念 deque是双端数组&#xff0c;而vector是单端的。 deque头部和尾部添加或移除元素都非常快速, 但是在中部安插元…

在x86/amd64的机器上使用Docker运行arm64v8/ubuntu并安装ROS1

一、准备工作 主要是因为国内网络的问题&#xff0c;可能导致镜像拉取失败&#xff0c;解决办法参考&#xff1a;镜像加速 二、安装运行过程 2.1拉取镜像&#xff1a; sudo docker pull arm64v8/ubuntu:20.04这个是ubuntu的拉取指令&#xff0c;其他的也是类似。 2.2 运行…

【DevOps】运维过程中经常遇到的Http错误码问题分析(一)

一、解决HTTP 408错误&#xff1a;上传3M文件时请求超时的问题 在开发Web应用程序时&#xff0c;遇到HTTP 408状态码&#xff08;请求超时&#xff09;是常见的问题。特别是在上传大文件时&#xff0c;这种情况更容易发生。本文将探讨在上传一个3M文件时&#xff0c;Web服务器…

LeetCode题练习与总结:排序链表--148

一、题目描述 给你链表的头结点 head &#xff0c;请将其按 升序 排列并返回 排序后的链表 。 示例 1&#xff1a; 输入&#xff1a;head [4,2,1,3] 输出&#xff1a;[1,2,3,4]示例 2&#xff1a; 输入&#xff1a;head [-1,5,3,4,0] 输出&#xff1a;[-1,0,3,4,5]示例 3&am…

封锁-封锁模式(共享锁、排他锁)、封锁协议(两阶段封锁协议)

一、引言 1、封锁技术是目前大多数商用DBMS采用的并发控制技术&#xff0c;封锁技术通过在数据库对象上维护锁来实现并发事务非串行调度的冲突可串行化 2、基于锁的并发控制的基本思想是&#xff1a; 当一个事务对需要访问的数据库对象&#xff0c;例如关系、元组等进行操作…

【嵌入式开发 Linux 常用命令系列 1.6 -- grep 过滤指定的目录】

请阅读【嵌入式开发学习必备专栏 】 文章目录 grep 过滤指定目录 grep 过滤指定目录 在Linux中使用grep搜索字符串并希望排除特定目录时&#xff0c;可以使用--exclude-dir参数。这个参数允许你指定一个或多个目录名称来排除它们的内容不被grep搜索。这对于忽略一些常见的临时…

LLM - 词向量 Word2vec

1. 词向量是一个词的低维表示&#xff0c;词向量可以反应语言的一些规律&#xff0c;词意相近的词向量之间近乎于平行。 2. 词向量的实现&#xff1a; &#xff08;1&#xff09;首先使用滑动窗口来构造数据&#xff0c;一个滑动窗口是指在一段文本中连续出现的几个单词&#x…

llamaindex实战-使用本地大模型和数据库对话

概述 本文使用NLSQLTableQueryEngine 查询引擎来构建SQL的自然语言处理查询。 请注意&#xff0c;我们需要指定要与该查询引擎一起使用的表。如果我们不这样做&#xff0c;查询引擎将提取所有架构上下文&#xff0c;这可能会溢出 LLM 的上下文窗口。 在以下情况都可以使用NL…

如何用Java写一个整理Java方法调用关系网络的程序

大家好&#xff0c;我是猿码叔叔&#xff0c;一位 Java 语言工作者&#xff0c;也是一位算法学习刚入门的小学生。很久没有为大家带来干货了。 最近遇到了一个问题&#xff0c;大致是这样的&#xff1a;如果给你一个 java 方法&#xff0c;如何找到有哪些菜单在使用。我的第一想…

线程中如何有效避免死锁问题

1. 理解死锁形成的原因 互斥条件&#xff1a;一个资源每次只能被一个线程使用。 请求与保持条件&#xff1a;线程因请求资源而阻塞时&#xff0c;对已获得的资源保持不放。 不剥夺条件&#xff1a;线程已获得的资源&#xff0c;在末使用完之前&#xff0c;不能强行剥夺。 循环…

c++ primer plus 第15章友,异常和其他:15.1.3 其他友元关系

c primer plus 第15章友&#xff0c;异常和其他&#xff1a;15.1.3 其他友元关系 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 15.1.3 其他友元关系 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可…

整洁架构SOLID-单一职责原则(SRP)

文章目录 定义案例分析重复的假象代码合并解决方案 小结 定义 SRP是SOLID五大设计原则中最容易被误解的一个。也许是名字的原因&#xff0c;很多程序员根据SRP这个名字想当然地认为这个原则就是指&#xff1a;每个模块都应该只做一件事。 在历史上&#xff0c;我们曾经这样描…

科研绘图系列:R语言双侧条形图(bar Plot)

介绍 双侧条形图上的每个条形代表一个特定的细菌属,条形的高度表示该属的LDA得分的对数值,颜色用来区分不同的分类群或组别,它具有以下优点: 可视化差异:条形图可以直观地展示不同细菌属在得分上的差异。强调重要性:较高的条形表示某些特征在区分不同组别中具有重要作用…

# Sharding-JDBC从入门到精通(6)-- Sharding-JDBC 水平分库 和 垂直分库。

Sharding-JDBC从入门到精通&#xff08;6&#xff09;-- Sharding-JDBC 水平分库 和 垂直分库。 一、Sharding-JDBC 水平分库-分片策略配置 1、分库策略定义方式如下 # 分库策略&#xff0c;如何将一个逻辑表映射到多个数据源 spring.shardingsphere.sharding.tables.<逻…

第33集《大乘起信论》

《大乘起信论》和尚尼慈悲&#xff0c;诸位法师、诸位居士&#xff0c;阿弥陀佛&#xff01;&#xff08;阿弥陀佛&#xff01;&#xff09;请大家打开《讲义》第七十四页&#xff0c;子二、释观。 本论的特色&#xff0c;一言以蔽之就是文简意赅、辞约理富&#xff0c;就是说…

VUE2拖拽组件:vue-draggable-resizable-gorkys

vue-draggable-resizable-gorkys组件基于vue-draggable-resizable进行二次开发, 用于可调整大小和可拖动元素的组件并支持冲突检测、元素吸附、元素对齐、辅助线 安装: npm install --save vue-draggable-resizable-gorkys 全局引用: import Vue from vue import vdr fro…

嵌入式linux面试1

1. linux 1.1. Window系统和Linux系统的区别 linux区分大小写windows在dos&#xff08;磁盘操作系统&#xff09;界面命令下不区分大小写&#xff1b; 1.2. 文件格式区分 windows用扩展名区分文件&#xff1b;如.exe代表执行文件&#xff0c;.txt代表文本文件&#xff0c;.…

运用Python与Keras框架打造深度学习图像分类应用:详尽步骤与代码实例解析

引言 随着深度学习技术的飞速发展&#xff0c;其在图像识别和分类领域的应用日益广泛。在这一背景下&#xff0c;Python因其丰富的数据科学库和强大的生态系统而成为首选编程语言之一。在本文中&#xff0c;我们将深入探讨如何使用Python和其中的Keras深度学习框架来完成一个实…

手动将dingtalk-sdk-java jar包打入maven本地仓库

有时候,中央镜像库不一定有自己需要的jar包,这时候我们就需要用到该方法,将jar打入maven本地仓库,然后项目中,正常使用maven的引入规则。 mvn install:install-file -Dmaven.repo.local=D:\software\maven\apache-maven-3.6.3-bin\apache-maven-3.6.3\repo -DgroupId=ding…