C语言——程序的编译与链接

翻译环境和执行环境

C语言的实现需要两种环境:

  • 把源代码转换为可执行的机器指令的翻译环境
  • 用于实际执行代码的执行环境

那什么是编译与链接呢?

翻译环境中:一个个源文件通过编译器转化为目标文件,多个目标文件再通过链接器形成一个可执行程序。(翻译过程如图)

 再细分,编译过程同样分为几个阶段。

  • 1.预处理 gcc -E test.c -o test.i
  • 2.编译 gcc -S test.c
  • 3.汇编 gcc -c test.c

我们先来看看执行环境做了什么,然后详细了解上边的过程。

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

预处理符号

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

#define

定义标识符

#define MAX 1000
#define reg register      //为 register这个关键字,创建一个简短的名字
#define do_forever for(;;)   //用更形象的符号来替换一种实现
#define CASE break;case     //在写case语句的时候自动把 break写上

定义宏

把参数替换到文本中,这种实现称为定义宏。

#define SQUARE( x ) x * x

但是,这个宏存在一个问题。

问题1

int a = 5;
printf("%d\n" ,SQUARE( a + 1) );

我们想实现的值是36,但实际输出是11,为什么呢?

 printf ("%d\n",a + 1 * a + 1 );

实际替换为5+1*5+1,为11.

如果想解决上述问题,就带上括号。

#define SQUARE(x) (x) * (x)

但下面出现了一个有趣的新问题

问题2

#define DOUBLE(x) (x) + (x)

int a = 5;
printf("%d\n" ,10 * DOUBLE(a));

我们期待打印出的是100,但实际输出55。

printf ("%d\n",10 * (5) + (5));

这个该如何解决呢?再加一个括号(两个问题本质都是解决优先级问题)

#define DOUBLE( x) ( ( x ) + ( x ) )

当字符串作宏参数时,我们可以把字符串放在字符串中。

##的作用

##可以将它左右的符号合成一个符号。

#define ADD_TO_SUM(num, value)
sum##num += value;
...
ADD_TO_SUM(5, 10);//作用是:给sum5增加10.

#define MAX(a, b) ( (a) > (b) ? (a) : (b) )
...
x = 5;
y = 8;
z = MAX(x++, y++);
printf("x=%d y=%d z=%d\n", x, y, z);//输出的结果是什么?

 因为实际中z = ( (x++) > (y++) ? (x++) : (y++));

x++一次,y++两次,并且将第一次++后的值赋予z。

宏和函数

宏通常用于执行简单的运算。比如比较大小

#define MAX(a, b) ( (a) > (b) ? (a) : (b) )

那为什么不用函数呢?

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

那宏就没有缺点了吗?

  • 1. 每次使用宏的时候,一份宏定义的代码将插入到程序中。除非宏比较短,否则可能大幅度增加程序的长度。
  • 2. 宏是没法调试的。
  • 3. 宏由于类型无关,也就不够严谨。
  • 4. 宏可能会带来运算符优先级的问题,导致程容易出现错。

我们将两者汇总成一张图表。

 

#undef

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

#undef NAME
//如果现存的一个名字需要被重新定义,那么它的旧名字首先要被移除。

启动编译

许多C 的编译器提供了一种能力,允许在命令行中定义符号。用于启动编译过程。 例如:当我们根据同一个源文件要编译出不同的一个程序的不同版本的时候,这个特性有点用处。

#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 -D ARRAY_SIZE=10 programe.c

文件包含

  • 本地文件

#include "filename"

  • 库文件包含

#include <filename.h>

猜你喜欢

转载自blog.csdn.net/qq_59392324/article/details/122143879