程序的编译和链接

本文详细探讨了C语言中的翻译环境和执行环境,介绍了预定义符号如__FILE__,__LINE__等,宏的使用、宏参数的副作用以及与函数的对比,讨论了优点和缺点,同时涵盖了条件编译和头文件包含的原理与问题解决方法。
摘要由CSDN通过智能技术生成

 

目录

翻译环境 

linux下的测试 ​编辑

预定义符号

执行环境

#define定义宏

#和##

#

##

宏参数的副作用

宏和函数对比

优点

缺点

#undef 

 条件编译

头文件包含


在标准c的任何实现中,存在两种环境——翻译环境和执行环境

翻译环境 

翻译环境生成目标文件(.obj)

linux下的测试 

预定义符号

__FILE__      //进行编译的源文件
__LINE__     //文件当前的行号
__DATE__    //文件被编译的日期
__TIME__    //文件被编译的时间
__STDC__    //如果编译器遵循ANSI C,其值为1,否则未定义

vs不完全支持c标准,Linux下的gcc支持c标准。 

执行环境

1. 程序必须载入内存中。在有操作系统的环境中:一般这个由操作系统完成。在独立的环境中,程序的载入必须由手工安排,也可能是通过可执行代码置入只读内存来完成。
2. 程序的执行便开始。接着便调用 main 函数。
3. 开始执行程序代码。这个时候程序将使用一个运行时堆栈(stack),存储函数的局部变量和返回地址。程序同时也可以使用静态(static )内存,存储于静态内存中的变量在程序的整个执行过程 一直保留他们的值。
4. 终止程序。正常终止 main 函数;也有可能是意外终止。

#define定义宏

#define 机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏 或定义宏
#define name( parament-list ) stuff
其中的 parament - list 是一个由逗号隔开的符号表,它们可能出现在 stuff 中。
注意:参数列表的 左括号必须与name紧邻
如果两者之间有任何空白存在,参数列表就会被解释为 stuff 的一部分。
#define SQUARE(X) X*X//err
SQUARE(5+1)--->5+1*5+1
//正确写法:
#define SQUARE(X) ((X)*(X))
注意:
1. 宏参数和 #define 定义中可以出现其他 #define 定义的符号(优先替换)。但是对于宏,不能出现递归
2. 当预处理器搜索 #define 定义的符号的时候,字符串常量的内容并不被搜索
#define SQUARE(X) ((X)*(X))
#define X 2//正确

#和##

#

当我们想实现下面这串代码时:

	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 f = 3.5f;
	printf("the value of f is %f\n", f);

有没有什么更简介的办法呢?

我们知道,c语言可以对字符串进行拼接,比如:

printf("hello"  "world");
//输出helloworld,省略了中间的空格。

修改后:

#define PRINT(val,format) printf("the value of "#val  "is " format "\n",val)
int a = 10;
PRINT(a, "%d");
	
int b = 20;
PRINT(b, "%d");
	
float f = 3.5f;
PRINT(f, "%f");

这里的#val相当于先对val完成替换再使之成为一个字符串("a")

##

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

宏参数的副作用

如果宏的参数在宏的定义中出现超过一次的话,如果参数带有副作用,将会导致不可预测的结果。

#define MAX(x,y) ((x)>(y)?(x):(y))

int main()
{
	int a = 3;
	int b = 4;
	int m = MAX(++a, ++b);

	int m = ((++a) > (++b) ? (++a) : (++b));
	printf("m = %d a=%d b=%d\n", m, a, b);

	return 0;
}

 输出结果为6,4,6

宏和函数对比

优点

        用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多。 所以宏比函数在程序的规模和速度方面更胜一筹
        更为重要的是函数的参数必须声明为特定的类型。
所以函数只能在类型合适的表达式上使用。反之这个宏怎可以适用于整形、长整型、浮点型等可以 用于> 来比较的类型。

 

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

int* p = MALLOC(int*,4);//define实现自定义封装

缺点

1.宏不能调试。

2.宏可能会带来运算符优先级问题。

3.宏由于类型无关,可能会导致程序不严谨。

4.如果宏定义的代码量过大,可能会大幅增加程序的长度

 

#undef 

作用:移去#define定义的对象

 

命令行定义

此方法适用于linux操作系统用命令的方式在程序编译的时候对未定义的变量进行定义的一种方式。 

#include <stdio.h>
int main()
{
    int array [ARRAY_SIZE];
    int i = 0;
    for(i = 0; i< ARRAY_SIZE; i ++)
   {
        array[i] = i;
   }
    for(i = 0; i< ARRAY_SIZE; i ++)
   {
        printf("%d " ,array[i]);
   }
    printf("\n" );
    return 0;
}

 命令行:
gcc test.c -D ARRAY_SIZE=10 

 条件编译

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

常见的条件编译语句:

#if 常量表达式
 //...
#endif
2.多个分支的条件编译
#if 常量表达式
 //...
#elif 常量表达式
 //...
#else
 //...
#endif
3.判断是否被定义
#if defined(symbol)
#ifdef symbol
#if !defined(symbol)
#ifndef symbol
4.嵌套指令
#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

与if..else很相似,不同的是条件编译会把不满足条件的代码在预处理阶段直接删除掉。 

头文件包含


本地文件包含:

#include "filename"

库文件包含:

#include <stdio.h>

嵌套包含:

comm.h comm.c 是公共模块。
test1.h test1.c 使用了公共模块。
test2.h test2.c 使用了公共模块。
test.h test.c 使用了 test1 模块和 test2 模块。
这样最终程序中就会出现两份 comm.h 的内容。这样就造成了文件内容的重复

解决方案:

开头写:

#prama once

或者

#ifndef __TEST_H__
#define __TEST_H__
//头文件的内容
#endif

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小C您好

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值