程序的编译和链接

 

目录

翻译环境 

linux下的测试 ​编辑

预定义符号

执行环境

#define定义宏

#和##

#

##

宏参数的副作用

宏和函数对比

优点

缺点

#undef 

 条件编译

头文件包含


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

程序的编译和链接_第1张图片

翻译环境 

程序的编译和链接_第2张图片

程序的编译和链接_第3张图片

程序的编译和链接_第4张图片

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

linux下的测试 程序的编译和链接_第5张图片

预定义符号

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

程序的编译和链接_第6张图片

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")

##

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

程序的编译和链接_第7张图片

宏参数的副作用

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

#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

宏和函数对比

优点

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

程序的编译和链接_第8张图片 

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

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

缺点

1.宏不能调试。

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

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

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

程序的编译和链接_第9张图片 

#undef 

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

 程序的编译和链接_第10张图片

命令行定义

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

#include 
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

程序的编译和链接_第11张图片

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

头文件包含


本地文件包含:

#include "filename"

库文件包含:

#include 

嵌套包含:

程序的编译和链接_第12张图片

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

你可能感兴趣的:(linux,运维,服务器,c语言)