【C语言从零到入门】第十五节 宏与多文件编程

一、宏

是根据一系列预定义的规则替换一定的文本模式,编译器在遇到宏时会自动进行这一模式替换。

1、宏的定义与使用

#define 宏名称 要替换的代码

使用的时直接使用宏的名称即可(类似变量的使用),在编译时首先会将宏名称替换为对应的代码。

例:

#define MY 666

printf("%d", MY);

以上代码在编译时,会被替换为:

printf("%d", 666);
  • 宏名称的命名规则与变量的命名规则相同

  • 宏定义通常被用来定义常量,如:

    #define PI 3.14159
    

    注意:宏定义的后面没有 “;”


2、宏函数

宏函数与普通函数类似。

例:

#define add(a, b) a+b

int d = 4;
int e = 5;
printf("%d", add(d, e));

以上代码在编译时,会被替换为:

#define add(a, b) a+b

int d = 4;
int e = 5;
printf("%d", d + e);
| 宏函数带来的问题 |

观察下面的代码,猜猜它会输出什么?

#include <stdio.h>

#define mul(a, b) a*b

void main()
{
    int d = 2;
	int e = 3;
    int f = mul(d+e, d+e);
    printf("%d", f);
}


可能会出乎你的意料,它的输出结果是 11 ,而不是25,这是为什么呢?

因为在编译时,代码首先会被替换为:

#include <stdio.h>

void main()
{
    int d = 2;
	int e = 3;
    int f = d+e * d+e;
    printf("%d", f);
}

那么,我们应该怎么避免这种情况发生呢?

正确的做法是:

在定义宏函数时,如果涉及到运算,需要为每个变量和宏函数添加括号。

例如上面的宏函数就应该改为:

#define mul(a, b) ((a)*(b))

二、多文件编程

在编程时,通常不会将所有的代码都写在一个文件里,而是把不同的功能写在不同的文件里,而主文件只需要调用对应的功能就可以了,这样做不仅可以使程序的结构更清晰,而且可以提高团队合作的效率。

1、头文件

头文件以 .h 为后缀名,其本质与 .c 文件一样,都是C语言的源文件,不过头文件通常只包含对变量和函数的定义,而不去进行实现。

如:

新建一个文件:library.h

#define PI 3.14159

float getCircleArea(float r);

2、include语句

a、基本格式

#include <文件名>

#include “文件名”

| 使用双引号与尖括号的区别|

  • 尖括号:直接在系统中寻找该文件
  • 双引号:先在当前目录中寻找对应文件,如果不存在则到系统中寻找
b、含义

与宏类似,在编译时,计算机会将include语句中的文件内容复制到当前文件。

例如:

新建一个文件:my.c(与 library.h 在同一文件夹下)

#include "lib.h"

int main()
{
    ......代码......
}

在编译时,会将 include 语句进行展开,所以 my.c 文件会变成:

#define PI 3.14159

float getCircleArea(float r);

int main()
{
    ......代码......
}

3、多文件编程

通常,我们在编写一个C语言程序时,会将各种功能封装成不同的函数放在对应的文件中,然后在mian.c中调用其他功能。

示例:

mian.c

#include <stdio.h>
#include "lib.h"

void sayHello();
float getInputFloat();
void mPrint(char*, float);

void main()
{
	sayHello();
	printf("请输入圆的半径:"); 
    float f = getInputFloat();
    mPrint("直径", getCircleDiam(f));
	mPrint("周长", getCircleLength(f));
	mPrint("面积", getCircleArea(f));
}

void sayHello()
{
	printf("输入圆的半径,可以获得圆的直径、周长和面积。\n");
	printf("注意:默认保留两位小数\n\n");
} 

float getInputFloat()
{
	float f;
	scanf("%f", &f);
	return f;
}

void mPrint(char *c, float f)
{
	printf("圆的%s是:%.2f\n", c, f);
}


lib.h

#define PI 3.14159

// 计算圆的直径 
float getCircleDiam(float r);

// 计算圆的面积 
float getCircleArea(float r);

// 计算圆的周长 
float getCircleLength(float r);



lib.c

#include "lib.h"

float getCircleDiam(float r)
{
	return 2 * r;  
}


float getCircleArea(float r)
{
	return PI * r * r;
}


float getCircleLength(float r)
{
	return 2 * PI * r;
}

运行结果:

在这里插入图片描述

三、条件编译

如果我们新建如下的三个文件

// a.h
int test = 666;

// b.h
#include "a.h"

// c.h
#include "a.h"

// mian.c
#include "a.h"
#include "c.h"
void mian()
{
    
}

点击运行,就会出现重复定义的错误:
在这里插入图片描述

为了防止这种情况发生,引入了条件编译语句。

1、条件编译语句基础

条件编译语句与if-else语句类似,不过它和if-else语句也有一些区别:

  • 条件编译语句可以写在文件中的任何位置,而if-else语句只能写在函数体中。

  • 条件编译语句是在编译时确定的,不符合条件的不会被编译到程序中,而if-else语句是在运行时确定的,无论是否符合条件,都会被编译到程序中。

  • 条件编译语句只能判断宏是否符合条件,if-else语句可以判断宏或变量是否符合条件

2、条件编译语句的语法

条件编译语句必须以条件编译判断语句开头,以#endif结尾。

条件编译判断语句:
语句意义
#if如果符合条件,则执行下列代码
#ifdef如果宏已定义,则执行下列代码
#ifndef如果宏未定义,则执行下列代码
#else语句

#else语句必须放在条件编译判断语句之后,类似普通的else语句,如果条件编译判断语句不成立,则执行下列代码。

#elif类似普通的else if语句。

示例:
#include <stdio.h>

#define NOW morning

#if NOW == morning
	#define HELLO "早上好"
#elif NOW == night
	#define HELLO "晚上好"
#else
	#define HELLO "你好"
#endif

void main()
{
	printf(HELLO); 
}
注意:

不能出现#else-#if 语句,必须用#elif代替。

// 这样写是不被允许的
#if A > 0
...
#else
#if A == 0
...
#else
...
    
    
// 必须这样写
#if A > 0
...
#elif A == 0
...
#else
...

3、应用

条件编译语句常用于解决上面提到的重复定义和重复包含问题。

上面代码的修改版:

// a.h
// LIB_A 是一个自定义的名称,尽量取得复杂一些,防止重复
#ifndef LIB_A
#define LIB_A
int test = 666;
#endif

// b.h
#include "a.h"

// c.h
#include "a.h"

// mian.c
#include "a.h"
#include "c.h"
void mian()
{
    
}

这样,就不会再出现重复定义和重复包含的错误。

在进行多文件编程时,一定要养成使用条件编译语句的习惯。

再见( ̄︶ ̄)↗

【C语言从零到入门】系列到此正式结束。

点击查看【C语言从零到入门】系列

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值