C语言 --- 程序环境和预处理

一、程序如何从代码变成可执行程序

 二、预编译详解

1.一些语言内置的预编译符号

//__FILE__   进行编译的源文件
//__LINE__   文件当前的行号
//__DATE__   文件被编译的日期
//__TIME__   文件被编译的时间
int main()
{
	printf("%s %s %s line=%d", __FILE__, __DATE__, __TIME__, __LINE__);
	return 0;
}

2.#define的使用

( 1 ) 可以定义标识符(简单来说就是用中间的标识符代替后面的代码)

#define MAX 100
#define forever for(;;)
#define PRINTF printf("%s %s %s %d", \
                    __FILE__, __DATE__, \
                    __TIME__, __LINE__)
//注意这里的\是续航符,当代码过长时,需要换行,就可以写\,后面必须紧跟换行

 ( 2 ) 可以用来定义宏(#define 机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏(macro)或定义宏)

举个例子:

#define ADD(x,y) x+y
int main()
{
	printf("%d",ADD(1,1));
	return 0;
}

 看完上面的代码,我们再来看看下面这段代码的结果

#define ADD(x,y) x+y
int main()
{
	printf("%d",5*ADD(1,1));//猜猜结果是啥?
	return 0;
}

我们绝大多数人都会认为结果是5*(1+1)=10,但真的会有这么简单吗?

下面我们揭晓答案

 那么这个结果是这么得到的呢?

我们将计算顺序变化一下,就会发现5*1+1不就是6吗?可是为什么会有这样的结果?

其实#define在预编译后,代码为

int main()
{
	printf("%d",5*1+1);
	return 0;
}

所以其实#define的功能仅仅只是替换,而不管优先级的问题,我们在写代码时,不要吝啬括号,写法如下

#define ADD(x,y) ((x)+(y))
int main()
{
	printf("%d",5*ADD(1,1));
	return 0;
}

如果还是不懂,可以看看下面#define的替换规则

在程序中扩展#define定义符号和宏时,需要涉及几个步骤。

  1. 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先被替换。
  2. 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值所替换。
  3. 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程。

注意:

  1. 宏参数和#define 定义中可以出现其他#define定义的符号。但是对于宏,不能出现递归。
  2. 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索

 三、#和##(较为少见,认识就行)

1.#

把一个宏参数变成对应的字符串

int main()
{
	int a = 10;
	printf("the value of a is %d\n", a);
	int b = 20;
	printf("the value of b is %d\n", b);
    float c = 3.14f;
    printf("the value of c is %f\n", c);
	return 0;
}

问:如何将上面代码printf("the value of __ is %__")这种固定的写法用一种简单的方式实现?

要解决这个问题,我们要了解printf这个函数的一个特点

先来看看下面两行代码输出结果一样吗?

printf("hello world\n");
printf("hello ""world\n");

 通过上诉代码,我们知道printf可以将多个字符串连起来输出,所以我们可以将printf("the value of __ is %__")中的字符串分开来考虑,下面我们看看代码

#define PRINTF(FORMAT, VALUE) \
        printf("the value of "#VALUE" is "FORMAT,VALUE)
int main()
{
	int a = 10;
	PRINTF("%d\n", a);
	int b = 20;
	PRINTF("%d\n", b);
	float c = 3.14f;
	PRINTF("%f\n", c);
	return 0;
}

2.##

可以把位于它两边的符号合成一个符号,它允许宏定义从分离的文本片段创建标识符

#define ADD(x,y) x##y
int ClassMax = 109;
int Class109 = 110;
int main()
{
	int a = 109;
	printf("%d\n", ADD(Class, 109));
	//printf("%d\n", ADD(Class, a));错误写法---因为没有标识符Classa
	printf("%d\n", ADD(Class, Max));
	return 0;
}

四、宏和函数

宏和函数在一定程度上很相似,下面讲一下他们之间的优劣

优点:

  • 宏比函数的运行效率更快(函数有创建栈帧,调用和返回的步骤,而宏仅仅是替换文本)
  • 宏的参数没有类型,比较灵活(甚至还可以传类型,而函数不行)

缺点:

  • 宏没有类型限制,不严谨
  • 如果宏较大,那么代码就很变得冗余,增加了代码的长度
  • 宏没法调试(宏在预编译的时候就会实现文本的替换,但我们无法观察到替换后的代码)
  • 宏不考虑优先级的问题,容易出错

写法上的规范:宏名一般全部大写,函数名一般不是全部大写

五、#undef和条件编译

1、#undef

可以移除#define定义的常量,也可以移除宏

#define MAX 1
#define ADD(x,y) ((x)+(y))
int main()
{
	printf("%d",MAX);//可以打印
	#undef MAX
	printf("%d",MAX);//不能打印

	printf("%d", ADD(1, 1));//可以打印
	#undef ADD
	printf("%d", ADD(1, 1));//不能打印
	return 0;
}


2、条件编译

#if (常量表达式)
//...
#elif (常量表达式)
//...
#else
//...
#endif

//如果定义了symbol
#if defined(symbol)
#ifdef symbol

#endif

//如果没定义symbol
#if !defined(symbol)
#ifndef symbol

#endif
//嵌套使用
#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

六、头文件的包含

#include<stdio.h>
//查找头文件直接去标准路径下去查找
#include"test.h"
//先在源文件所在目录下查找,如果该头文件未找到,编译器就像查找库函数头文件一样在标
//准位置查找头文件。



//为了防止头文件的嵌套包含
//需要在头文件开头加上
#pragma once
//内容

//或

#ifndef __TEST_H__
#define __TEST_H__
//内容
#endif
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值