程序的编译+链接

目录

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

2.详解编译+链接

(1)翻译环境

整体过程:

每步详解:

 (2)运行环境

 3.预处理详解

(1)预定义符号

(2)#define

1 #define 定义标识符

2 #define 定义宏

3 #define 替换规则

 4 #和##

 5 带副作用的宏参数

 6 宏和函数对比

(3) #undef

(4)命令行定义 

 (5) 条件编译

常见的条件编译指令:

 (6) 头文件的包含


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

 

 

2.详解编译+链接

(1)翻译环境

整体过程:

每步详解:

这里来讲解一下符号汇总,形成符号表,符号表的合并和重定位三大过程;

 符号汇总:

汇总的都是全局的符号.

形成符号表,符号表的合并和重定位:

看这个例子:

一个加法函数分两个源文件来写,在汇编过程中会形成各自的符号表,以上例子中Add.c中定义了add函数,在Add.o中会形成自己的符号表,并且有它的地址;test.c文件中使用extern声明了Add函数,test.o中会形成main函数的符号表和地址,Add的符号表和地址;在进行符号表合并时,test.o中Add的地址是无效的,因为它只是声明没有定义,所以符号表合是合并main和Add.o中Add的符号表。

 由此引出了以下的一种错误,只声明不定义:

这是一种链接型错误

 (2)运行环境

 3.预处理详解

(1)预定义符号

这些预定义符号都是语言内置的(VS编译器是不遵循ANSI C的)

int main()
{
	printf("%s\n", __FILE__);     //进行编译的源文件
	printf("%d\n", __LINE__);     //文件当前的行号
	printf("%s\n", __DATE__);     //文件被编译的日期
	printf("%s\n", __TIME__);     //文件被编译的时间
	printf("%d\n", __STDC__);     //如果编译器遵循ANSI C,其值为1,否则未定义

	return 0;
}

(2)#define

1 #define 定义标识符

#define 定义标识符会在预编译阶段完成替换

例子:

#define MAX 100;
#define STR "bitejiuyeke"
#define do_forever for(;;)

问题:在define定义标识符的时候,要不要在最后加上?   建议不要加上; ,这样容易导致问题。

如:

#define MAX 100;
int main()
{

	int m = 0;

	if (3 > 5)
		m = MAX;
	else
		m = -1;

	return 0;
}

m=MAX;这里会出现语法错误

2 #define 定义宏

规则:

 例子:

#define MAX(x,y) (x>y?x:y)
int main()
{
	int a = 10;
	int b = 20;
	int m = MAX(a, b);
	printf("%d\n", m);

	return 0;
}

一个小坑:

一个相乘函数原本想算出结果30,实际计算的结果是11,没有达到预期效果

#define SQUARE(x) x*x
int main()
{
	int ret = SQUARE(5 + 1);   //int ret = 5+1*5+1;
	return 0;
}

解决:  写成这样#define SQUARE(x) ((x)*(x))

所以用于对数值表达式进行求值的宏定义都应该用这种方式加上括号,避免在使用宏时由于参数中
的操作符或邻近操作符之间不可预料的相互作用。

3 #define 替换规则

 4 #和##

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

 一个例子:

 修改:

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

例子:

 5 带副作用的宏参数

 例子:

想要求出3和4谁大,却求出了别的结果

 6 宏和函数对比

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

如:

#define MALLOC(num,type)  (type*)malloc(num*sizeof(type))

int main()
{
	int* p = (int*)malloc(10 *sizeof(int));
	MALLOC(10, int);

	return 0;
}

 命名约定

但是有例外: offsetof    getchar   都是宏

(3) #undef

 

(4)命令行定义 

这里不过多介绍 

 (5) 条件编译

在编译一个程序的时候我们如果要将一条语句(一组语句)编译或者放弃是很方便的。因为我们有条件
编译指令。
比如说:
调试性的代码,删除可惜,保留又碍事,所以我们可以选择性的编译。

例子:

如果定义了__DEBUG__,就打印出arr数组元素中的每个值,否则不打印

#define __DEBUG__

int main()
{
	int i = 0;
	int arr[10] = { 0 };
	for (i = 0; i < 10; i++)
	{
		arr[i] = i;
#ifdef __DEBUG__
		printf("%d\n", arr[i]);    //为了观察数组是否赋值成功。
#endif 
	}
	return 0;
}
//#define __DEBUG__

int main()
{
	int i = 0;
	int arr[10] = { 0 };
	for (i = 0; i < 10; i++)
	{
		arr[i] = i;
#ifdef __DEBUG__
		printf("%d\n", arr[i]);    //为了观察数组是否赋值成功。
#endif 
	}
	return 0;
}

常见的条件编译指令:

例子:

 即使#if #elif条件都满足也只会进入#if语句打印hehe, 用法类似于if-else if-else语句,只能进入一条语句中

注意:

都是判断是否有定义,前两个功能相同,后两个功能相同

#if defined(symbol)
#ifdef symbol


#if !defined(symbol)
#ifndef symbol 

 (6) 头文件的包含

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值