c语言程序的预处理

简介: c语言程序的预处理

程序的翻译环境

程序的执行环境

详解C语言程序的编译+链接

预定义符号

预处理指令#define

宏和函数的对比

预处理操作符#和##的介绍

命令定义

预处理指令#include

 

程序的翻译环境

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

在任何c的实现中都存在两种不同的环境

1.翻译环境就是在这环境中源代码被转换为可执行的机器指令

2.执行环境就是实际执行代码



程序的执行环境3.运行环境

1).程序必须载入内存中,在有操作系统的环境中,这一般由操作系统完成,

在独立的环境中,程序的载入必须由手工安排,也可能通过可执行代码置入只读内存来完成(嵌入式)

2).程序的执行便开始,接着调用main函数

3).开始执行程序代码,这时候程序将使用一个运行时的堆栈(栈帧)(并不包括堆),在使用完后就将他的地址回收,存储函数的局部变量和返回地址,

程序同时也可以使用静态(static)内存,存储与静态内存中的变量在程序的整个执行过程一直保存他们的值。

4).终止程序,正常终止main函数,也可能意外终止


详解C语言程序的编译+链接



预定义符号

__FILE__打印当前文件所在的路径

__LINE__打印当前代码所在函数

__DATE__打印当前日期

__TIME__打印当前时间

预处理指令#define

define定义符号,可以提升代码的可读性

不仅可以定义数字,也可以定义代码,关键字,或者符号,

#define m 100
#define ret register//用ret来替换registr
#define do_forever for( ; ;)//死循环
#define x int*
#include<stdio.h>
int main()
{
    ret int b = 10;
    do_forever;
    int a = m;//100
    x p=&a;//p为int*的指针变量
    return 0;
}
#define x int *
typedef int * INT
#include<stdio.h>
int main()
{
x a,b;
INT c,d;
//其中只有b不是指针
//因为define只完成代码的替换,替换成了int* a,b//a是指针,b不是指针
//而typedef是对类型的重命名,
return 0;
}

宏和函数的对比

define 定义宏,同样是完成替换

#define机制包括一个规定,把常数替换到文本中,这种实现形式就叫做宏,或定义宏,

参数左括号必须与函数名紧邻,若中间又有空格,就会被解释为stuff中的成员,不可吝啬括号

#include<stdio.h>
//不可以吝啬括号
#define square(x) x*x//建议把x加个括号(x),将其当做一个整体,括号很重要
#define double(x) (x)+(x)//将之当成一个整体才可以
#define doubl(x) ((x)+(x))
int main()
{
  printf("%d\n", square(3));//会被替换成printf("%d",3*3);
  printf("%d\n", square(3 + 1));//7!=16,会被替换,宏的参数是完成替换的,不是计算的,不经过任何计算直接传过去
  //会被替换成printf("%d",3+1*3+1))//7
  //加括号就变成了(3+1)*(3+1)
  printf("%d", 10 * double(4));//这样写也是有问题的,会被替换成10*(4)+(4)=44
  printf("%d", 10 * doubl(4));//这样就可以了;
  return 0;
}

再调用宏的时候,首先对参数进行检查,是否有define定义的符号

宏不能递归,而函数可以

当预处理时,字符串常量不能被替换,如printf(“”)里的不被替换


#define m 100
#include<stdio.h>
int main()
{
  int c = max(101, m);//宏替换,
  printf("m=%d", m);//括号里面的m不被替换
  return 0;
}


预处理操作符#和##的介绍

#可以把参数插入到字符串中

#define print(x) printf("the value of " #x " is %d\n",x)//#x会变成x对于的内容所对于的字符串"a"
#include<stdio.h>
int main()
{
  printf("hello world\n");
  printf("hello""world\n");//结果是一样的,
  //写3个printf()有点冗余,
  int a = 10;
  print(a);
  //the value of a is 10
  int b = 20;
  print(b);
  //the value of b is 20
  int c = 30;
  print(c);
  //the value of b is 30
  printf("");
  return 0;
}


##可以把两个符号合成一个符号,两个符号连在一起

#define cd(x,y) x##y
#include<stdio.h>
int main()
{
  int c = 100;
  printf("%d", cd(c, 120));//替换成100120
  return 0;
}

带副作用的宏参数

首先介绍一下什么是副作用

int a=1;

int b=a+1;//b=2,a=1,没有副作用

int b=++a//b=2,而a=2这里的a就是有副作用,

#include<stdio.h>
#define max(x,y) (x)>(y)?(x):(y)
int main()
{
  int a = 5;
  int b = 8;
  int c = max(a++, b++);
  //int c = (a++) > (b++) ? (a++) : (b++);替换
  //5>8不成立,执行完判断语句,a和b就加1,a=6,b=9,则执行b++,但b++是先使用在++
    //c=9,执行完后b=10
  printf("%d", c);//9
  return 0;
}


宏和函数的对比


1.宏比函数在程序的规模上和速度上更胜一筹

2.更为重要的是,函数在定义上有数据类型限制,若定义int型数据,则double型数据就无法传参,而宏与类型无关,


3.命名规定,宏名全部大写,函数名不全部大写(不成文的规定)


命令定义


#undef移除一个宏定义


#if 加常量表达式,为0为真往下面执行


#endif为#if的结束标志


#ifdef 如果定义了就执行下面的代码


#ifndef 如果没定义就执行下面的代码


#undef//移除一个宏定义
#define m 100
#include<stdio.h>
int main()
{
  int a = m;
#undef m//把定义的m取消掉
  printf("%d", a);//a就没有值
  return 0;
}
//#if 加常量表达式(非0未真就往下执行,直到#endif停止)
//#endif为#if的结束标志
//#if 0//相当于把下面的代码给注释掉,
#define print 1
int main()
{
#if 1-2//1为真就执行,0就为假不执行,非0为真
#endif  
  #if print//print为1为真就往下执行下面的代码
  printf("hehe ");
#endif
  return 0;
}
#include<stdio.h>
int main()
{
#if 1==1//判断成立,就往下执行,从多分支只选择一个,选择完下面就不执行跳到#endif去
  printf("hh");
  #elif 1 == 2
    printf("haha");
  #else
    printf("hehe");
#endif
  return 0;
}
//判断是否被定义的写法
#include<stdio.h>
int main()
{
#ifdef test//如果test被定义了,下面参与执行,给个0也可以
  printf("test");
#endif
#ifndef hh//如果hh不定义,下面参与编译
  printf("hehe");
#endif
  return 0;
}

预处理指令#include

文件包含

1.<>直接去库函数的头文件所在的目录下查找

2.""形式的包含文件,(1).在自己写的代码底下所在的目录去查找,(2).如果1找不到,就在库函数的头文件目录下查找


嵌套文件包含

一个头文件被重复包含两次,有点啰嗦

#pragma once//头文件只包含一次,不管自己写了多少此,加上这句都只用一次

头文件中的ifndef,define ,endif是用来防止文件被多次包含


相关文章
|
2月前
|
存储 自然语言处理 编译器
【C语言】编译与链接:深入理解程序构建过程
【C语言】编译与链接:深入理解程序构建过程
|
2月前
|
编译器 C语言
C语言--预处理详解(1)
【10月更文挑战第3天】
|
2月前
|
编译器 Linux C语言
C语言--预处理详解(3)
【10月更文挑战第3天】
|
24天前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
45 5
|
24天前
|
C语言
C语言编程中,错误处理至关重要,能提升程序的健壮性和可靠性
C语言编程中,错误处理至关重要,能提升程序的健壮性和可靠性。本文探讨了C语言中的错误类型(如语法错误、运行时错误)、基本处理方法(如返回值、全局变量、自定义异常处理)、常见策略(如检查返回值、设置标志位、记录错误信息)及错误处理函数(如perror、strerror)。强调了不忽略错误、保持处理一致性及避免过度处理的重要性,并通过文件操作和网络编程实例展示了错误处理的应用。
57 4
|
23天前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
54 1
|
24天前
|
网络协议 物联网 数据处理
C语言在网络通信程序实现中的应用,介绍了网络通信的基本概念、C语言的特点及其在网络通信中的优势
本文探讨了C语言在网络通信程序实现中的应用,介绍了网络通信的基本概念、C语言的特点及其在网络通信中的优势。文章详细讲解了使用C语言实现网络通信程序的基本步骤,包括TCP和UDP通信程序的实现,并讨论了关键技术、优化方法及未来发展趋势,旨在帮助读者掌握C语言在网络通信中的应用技巧。
35 2
|
24天前
|
程序员 C语言
C语言中的指针既强大又具挑战性,它像一把钥匙,开启程序世界的隐秘之门
C语言中的指针既强大又具挑战性,它像一把钥匙,开启程序世界的隐秘之门。本文深入探讨了指针的基本概念、声明方式、动态内存分配、函数参数传递、指针运算及与数组和函数的关系,强调了正确使用指针的重要性,并鼓励读者通过实践掌握这一关键技能。
36 1
|
1月前
|
C语言
【c语言】你绝对没见过的预处理技巧
本文介绍了C语言中预处理(预编译)的相关知识和指令,包括预定义符号、`#define`定义常量和宏、宏与函数的对比、`#`和`##`操作符、`#undef`撤销宏定义、条件编译以及头文件的包含方式。通过具体示例详细解释了各指令的使用方法和注意事项,帮助读者更好地理解和应用预处理技术。
27 2
|
2月前
|
编译器 Linux C语言
【C语言篇】编译和链接以及预处理介绍(下篇)
【C语言篇】编译和链接以及预处理介绍(下篇)
37 1
【C语言篇】编译和链接以及预处理介绍(下篇)