程序环境和预处理

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

在ANSI C的任何一种实现中,存在两个不同的环境

  • 第一种是翻译环境,翻译环境会把源代码翻译成可执行的代码

  • 第二种是执行环境,他是用来执行代码

2. 编译加链接

  • 编译----->链接

  • 编译:预编译/预处理---->编译---->汇编

    • 预处理:处理 1.#include 的文件 2.#define 定义的符号的替换 (删除定义的符号)3. 删除注释

    • 编译:语法分析、词法分析、语义分析、符号汇总

    • 汇编:把汇编代码转换成二进制指令

  • 运行环境:

    1. 程序必须载入内存中:
    - 在有操作系统的环境中,是有操作系统完成
    - 在独立的环境中程序的载入是手工完成,也可能是可执行代码置入只读内存来完成.

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

3.开始执行代码,就会运行函数堆栈

3.预处理

3.1预定义符号

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

3.2#define

1️⃣#define定义标识符

#define name stuff
  • 如果定义的stuff过长,那么可以分成多行的内容,除了最后一行,每行都要输入反斜杠符号\

  • 在#define定义的内容后不应加;,例如:

#define MAX 10
#define MAX 10;

上一行代码,会把MAX看作10

如果放置了;,编译器会把MAX看作10;

2️⃣#define定义宏

#define name( parament-list ) stuff
  • 参数列表的左括号必须与name紧邻。

  • 如果两者之间有任何空白存在,参数列表就会被解释为stuff的一部分。

  • 不能吝啬放括号,例如:

    • 想输出的是36(6*6),而实际输出的是11(5+1*5+1),所以我们需要调整

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

      调整成#define SQUARE(x) (x)*(x)

    • 想要输出100,而实际输出的是55

      #define DOUBLE(x) (x)+(x)
      int a=5;
      printf("%d\n",10*DOEBLE(a));

      所以在这个基础上我们需要再加上括号,变成:#define DOUBLE(x) ((x)+(x))

    3️⃣#define 替换规则

    • 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先被替换。

    • 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值所替换。

    • 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程。

    4️⃣#和##

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

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

    5️⃣带有副作用的宏参数

    • 在使用++时容易发生错误,在下面这个函数中,实际如图,b实际++两次

    #define MAX(x,y) (x>y)?(x):(y))
    int main()
    {
        int a=2;
        int b=3;
        m=MAX(a++,b++);
        printf("%d",m);
        printf("%d %d",a,b);
        return 0;
    }

     

6️⃣宏和函数的对比

1.在简单的运算中,宏比较方便,而函数需要调用

2.宏与类型无关,在浮点型,整数型在宏中都可以运用,函数需要定好类型

3.宏可以传类型,函数不可以传类型

4.宏不能递归,函数可以递归

5.宏是没法调试的。函数可以调试

6.宏由于类型无关,也就不够严谨。

7.宏可能会带来运算符优先级的问题,导致程容易出现错。宏需要多添加括号

3.3 #undef

用于移除一个宏定义

如果一个存在的名字需要重新定义,就需要移除他的旧定义

#undef NAME

3.4命令行定义

许多C 的编译器提供了一种能力,允许在命令行中定义符号。用于启动编译过程。

例如:当我们根据同一个源文件要编译出不同的一个程序的不同版本的时候,这个特性有点用处。(假定某个程序中声明了一个某个长度的数组,如果机器内存有限,我们需要一个很小的数组,但是另外一个机器内存大写,我们需要一个数组能够大写。)

3.5条件编译

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

  • 单支编译

#if 常量表达式
//...
#endif
//常量表达式由预处理器求值。
如:
#define __DEBUG__ 1
#if __DEBUG__
//..
#endif
  • 多个分支的条件编译

#if 常量表达式
//...
#elif 常量表达式
//...
#else
//...
#endif
  • 判断是否被定义

#if defined(symbol)
#ifdef symbol
#if !defined(symbol)
#ifndef symbol
  • 嵌套指令

#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

3.6 文件包含

#include指令可以使另一个文件被编译,但是为了防止一些文件被多次使用

  • 第一种方法:

#ifndef __TEST_H__
#define __TEST_H__
//函数
#endif
  • 第二种方法:

#pragma once

在引文件时,有#include<stdio.h>和#include“stdio.h”

<>和""的查找的策略不一样

  • <>直接去提供的库目录下查找

  • ""查找策略:先去代码所在的路径下查找,如果找不到,

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值