C【程序环境和预处理】

1. 程序的翻译环境和执行环境

2. 详解编译+链接

2.1 翻译环境

2.2 编译本身也分为几个阶段

sum.c

复制代码
int g_val = 2016;
void print(const char *str)
{
 printf("%s\n", str);
}

test.c

cpp 复制代码
#include <stdio.h>
int main()
{
 extern void print(char *str);
 extern int g_val;
 printf("%d\n", g_val);
 print("hello bit.\n");
 return 0;
}

图解

符号表和重定向

2.3 运行环境

3. 预处理详解

3.1 预定义符号

cpp 复制代码
int main()
{
	//printf("%s\n", __FILE__);//本文件所在的绝对路径
	//printf("%d\n", __LINE__);//当前代码所在的行数
	//printf("%s\n", __DATE__);//当前日期
	//printf("%s\n", __TIME__);//当前时间

	//д־ļ
	int i = 0;
	int arr[10] = { 0 };
	//表示通过【写】的形式记录到log.txt文件中
	FILE* pf = fopen("log.txt", "w");
	for (i = 0; i < 10; i++)
	{
		arr[i] = i;
		fprintf(pf, "file:%s line:%d date:%s time:%s i=%d\n", 
			__FILE__, __LINE__, __DATE__, __TIME__, i);
		printf("%s\n", __FUNCTION__);
	}
	fclose(pf);
	pf = NULL;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}

	return 0;
}

3.2 #define:总结【记得加括号,整个替换】

3.2.1 #define 定义标识符

cpp 复制代码
#define MAX 1000
#define reg register          //为 register这个关键字,创建一个简短的名字
#define do_forever for(;;)     //用更形象的符号来替换一种实现
#define CASE break;case        //在写case语句的时候自动把 break写上。
// 如果定义的 stuff过长,可以分成几行写,除了最后一行外,每行的后面都加一个反斜杠(续行符)。
#define DEBUG_PRINT printf("file:%s\tline:%d\t \
                          date:%s\ttime:%s\n" ,\
__FILE__,__LINE__ ,       \
__DATE__,__TIME__ ) 
cpp 复制代码
#define MAX 100
#define STR "hehe"

#define reg register
//for循环
#define do_forever for(;;)

int main()
{
	for (;;)
		;

	return 0;

	//reg int a;
	//register int a;
	//int max = MAX;
	//int max = 100;
	//printf("%d\n", max);
	//printf("%s\n", STR);
}

提问:

在define定义标识符的时候,要不要在最后加上 ; ?

建议不要加上 ; ,这样容易导致问题。

3.2.2 #define 定义宏

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

下面是宏的申明方式:

#define name( parament-list ) stuff

其中的 parament-list 是一个由逗号隔开的符号表,它们可能出现在stuff中

注意点:

cpp 复制代码
int main()
{
	int a = 5;
	int ret = 10 * DOUBLE(X);
	printf("%d\n", ret);//ret=10* 5+5=55

	return 0;
}
cpp 复制代码
#define DOUBLE(X) ((X)+(X))

int main()
{
	int a = 5;
	int ret = 10 * DOUBLE(X);//10*(5+5)=100
	printf("%d\n", ret);

	return 0;
}

在进行宏定义的时候,如果是表达式,则一定要在变量外面加上括号

3.2.3 #define 替换规则

注意:

  1. 宏参数和#define 定义中可以出现其他#define定义的变量。但是对于宏,不能出现递归。

  2. 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。

3.2.4

解决方案:

cpp 复制代码
// 使用 # ,把一个宏参数变成对应的字符串
#define PRINT(X) printf("the value of " #X " is %d\n", X)

int main()
{
	//printf("hello world\n");
	//printf("hello " "world\n");
	//printf("hel" "lo " "world\n");

	int a = 10;
	int b = 20;
	PRINT(a);
	//printf("the value of ""a"" is %d\n", a);
	PRINT(b);
	//printf("the calue of ""b"" is %d\n", b);
	return 0;
}

3.2 5

##可以把位于它两边的符号合成一个符号。

它允许宏定义从分离的文本片段创建标识符

cpp 复制代码
int main()
{
	int Class84 = 2019;
	//printf("%d\n", class84);
	printf("%d\n", CAT(Class, 84));//2019
	//printf("%d\n", Class##84);
	//printf("%d\n", Class84);
	return 0;
}

3.2 .6 带副作用的宏参数

MAX宏可以证明具有副作用的参数所引起的问题。

cpp 复制代码
//宏定义是整个替换
#define MAX(X, Y)     ((X)>(Y)?(X):(Y))

int main()
{
	int a = 10;
	int b = 11;

	int max = MAX(a++, b++);
	//int max = ((a++) > (b++) ? (a++) : (b++));
	//				10    11      11      12
	printf("%d\n", max);//12
	printf("%d\n", a);//11
	printf("%d\n", b);//13

	return 0;
}

3.2.7 宏和函数对比

使用宏的优点

适合代码简单的

cpp 复制代码
//函数-1
int Max(int x, int y)
{
	return (x > y ? x : y);
}

float Max2(float x, float y)
{
	return (x > y ? x : y);
}

//宏-2
#define MAX(X, Y) ((X)>(Y)?(X):(Y))

int main()
{
	int a = 10;
	int b = 20;
	float c = 3.0f;
	float d = 4.0f;
	//函数在调用的时候
	//会有函数调用和返回的开销
	//
	float max = Max2(c, d);
	printf("max = %d\n", max);
	//预处理阶段就完成了替换
	//没有函数的调用和返回的开销
	max = MAX(c, d);
	max = ((c) > (d) ? (c) : (d));
	printf("max = %d\n", max);
	/*int max = Max(a, b);
	printf("max = %d\n", max);
	max = MAX(a, b);
	printf("max = %d\n", max);*/

	return 0;
}

使用宏的缺点

不适合要进行多次循环的

宏有时候可以做函数做不到的事情。比如:宏的参数可以出现类型,但是函数做不到。

cpp 复制代码
#define MALLOC(num, type) (type*)malloc(num*sizeof(type))
int main()
{
	int* p = (int*)malloc(10*sizeof(int));
	
	int* p = MALLOC(10, int);
	//int* p = (int*)malloc(10 * sizeof(int));
	return 0;
}


#define SIZEOF(type) sizeof(type)

int main()
{
	int ret = SIZEOF(int);
	//int ret = sizeof(int);
	printf("%d\n", ret);
	return 0;
}

命名约定

把宏名全部大写

函数名不要全部大写

3.3 #undef

这条指令用于移除一个宏定义

cpp 复制代码
#define MAX 100

int main()
{
	printf("MAX = %d\n", MAX);
	//移除宏定义
#undef MAX
	printf("MAX = %d\n", MAX);

	return 0;
}

3.4 命令行定义

许多C 的编译器提供了一种能力,允许在命令行中定义符号。用于启动编译过程。 例如:当我们根据同一个源文件要编译出不同的一个程序的不同版本的时候,这个特性有点用处。(假 定某个程序中声明了一个某个长度的数组,如果机器内存有限,我们需要一个很小的数组,但是另外一 个机器内存大写,我们需要一个数组能够大写。)

3.5 条件编译: #ifdef DEBUG

在编译一个程序的时候我们如果要将一条语句(一组语句)编译或者放弃是很方便的。因为我们有条件 编译指令。

cpp 复制代码
//条件编译:想要编译哪一些代码可以选择
int main()
{
	int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
	int i = 0;

	for (i = 0; i < 10; i++)
	{
		arr[i] = 0;
#ifdef DEBUG
		printf("%d ", arr[i]);
#endif
	}

	return 0;
}

常见的条件编译指令:

1.#if 常量

cpp 复制代码
int main()
{
	int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
	int i = 0;

	for (i = 0; i < 10; i++)
	{
		arr[i] = 0;
//if 1【为真,则进行编译】
//if 0【为假,不进行编译】
#if 1+1
		printf("%d ", arr[i]);
#endif
	}

	return 0;
}

2.多分支的条件编译

cpp 复制代码
int main()
{
#if 1==2
	printf("haha\n");
#elif 2==2
	printf("hehe\n");
#else
	printf("嘿嘿\n");
#endif

	return 0;
}

3.判断是否被定义

cpp 复制代码
#define DEBUG 0

int main()
{
//如果定义过DEBUG,则执行
//方法一:
#if defined(DEBUG)
	printf("hehe\n");
#endif
//方法二:
#ifdef DEBUG
	printf("hehe\n");
#endif

//如果没有定义过DEBUG,则执行
#if !defined(DEBUG)
	printf("hehe\n");
#endif

#ifndef DEBUG
	printf("hehe\n");
//#endif
	return 0;
}

4.嵌套指令

3.6 文件包含

3.6.1 头文件被包含的方式:

先在源文件所在目录下查找,如果该头文件未找到,编译器就像查找库函数头文件一样在标 准位置查找头文件。 如果找不到就提示编译错误。

自定义文件使用""双引号

3.6.2 嵌套文件包含

解决方法:

cpp 复制代码
#ifndef __TEST_H__
#define __TEST_H__
//头文件的内容
#endif   //__TEST_H__

或者

cpp 复制代码
#pragma once

4. 其他预处理指令

cs 复制代码
#error
#pragma
#line
相关推荐
m0_748708056 小时前
C++中的观察者模式实战
开发语言·c++·算法
qq_537562676 小时前
跨语言调用C++接口
开发语言·c++·算法
wjs20246 小时前
DOM CDATA
开发语言
Tingjct6 小时前
【初阶数据结构-二叉树】
c语言·开发语言·数据结构·算法
猷咪6 小时前
C++基础
开发语言·c++
IT·小灰灰6 小时前
30行PHP,利用硅基流动API,网页客服瞬间上线
开发语言·人工智能·aigc·php
快点好好学习吧6 小时前
phpize 依赖 php-config 获取 PHP 信息的庖丁解牛
android·开发语言·php
秦老师Q6 小时前
php入门教程(超详细,一篇就够了!!!)
开发语言·mysql·php·db
烟锁池塘柳06 小时前
解决Google Scholar “We‘re sorry... but your computer or network may be sending automated queries.”的问题
开发语言
是誰萆微了承諾6 小时前
php 对接deepseek
android·开发语言·php