知识导图 知识导图
首页
iOS知识
计算机软件
  • 即时通讯网 (opens new window)
  • 开发常用网站 (opens new window)
首页
iOS知识
计算机软件
  • 即时通讯网 (opens new window)
  • 开发常用网站 (opens new window)
  • MD

  • Vue

  • C语法

    • C语法Day1-函数
    • C语法Day2-运算
    • C语法Day3-结构
    • C语法Day4-指针
    • C语法Day5-预处理
      • 头文件引用
        • 说明
        • 引用
        • 头文件编译
      • 预处理器
        • 说明
        • 避免重复引用
        • #ifdef 和 #ifndef 命令
        • 条件编译
        • 预定义宏
        • 预处理器运算符
        • 参数化的宏
      • 别名
        • 1、未使用别名的结构体:
        • 2、使用别名的结构体:
    • C语法Day6-文件
    • C语法Day7-标准库
    • C语法Day8-编译
  • C++语法

  • 汇编语言

  • 软件编程及算法
  • C语法
2023-05-28
目录

C语法Day5-预处理

# 头文件引用

# 说明

1、函数声明和宏定义单独放在.h文件中,供其他文件 #include 引用

2、编译的过程中,会将 .h 文件内容直接 复制一份 到被引用的文件中供使用,就是这么简单粗暴

3、include : 编译预处理指令

# 引用

使用预处理指令 #include 可以引用用户和系统头文件。它的形式有以下两种:

// 使用尖括号引用头文件,一般引用系统提供的文件
// 编译时优先搜索系统目录的标准列表
#include <file>

// 使用引号引入文件,一般指用户自定义文件
// 编译时优先搜索当前文件的目录
#include "file"

# 头文件编译

Animal.h 文件:

char *eat(void);

主程序文件:main.c

// 引用头文件
#include "Animal.h"

int main (void)
{
   puts (eat());
}

编译器编译过程:main.c

// 复制 Animal.h 中的内容到当前文件,并替换掉  #include "Animal.h"
char *eat(void);

int main (void)
{
   puts (eat());
}

# 预处理器

# 说明

C 预处理器:不是编译器的组成部分,但是它是编译过程中一个单独的步骤。

⚠️ 简单理解是,可以通过字符串形式编写或者拼接代码,在编译之前,会通过替换工具将其替换成真正的代码块,并插入到对应的位置,完成之后,后面编译才会检查其语法。

所有的预处理器命令都是以井号(#)开头。它必须是第一个非空字符,为了增强可读性,预处理器指令应从第一列开始。下面列出了所有重要的预处理器指令:

指令 描述
#define 定义宏
#include 包含一个源代码文件
#undef 取消已定义的宏
#ifdef 如果宏已经定义,则返回真
#ifndef 如果宏没有定义,则返回真
#if 如果给定条件为真,则编译下面代码
#else #if 的替代方案
#elif 如果前面的 #if 给定条件不为真,当前条件为真,则编译下面代码
#endif 结束一个 #if……#else 条件编译块
#error 当遇到标准错误时,输出错误消息
#pragma 使用标准化方法,向编译器发布特殊的命令到编译器中

# 避免重复引用

头文件引用在编译时直接复制替换,如果重复引用到话会导致重复copy :

#ifndef HEADER_FILE
#define HEADER_FILE

头文件代码,宏定义,include 等等

#endif

ifndef :判断是否定义过的标记,定义过,避免重复

格式:

# #ifdef 和 #ifndef 命令

上面代码可以发现,我们可以通过 #ifdef 和 #ifndef 命令测试某个宏是否已被定义。它们的语法是:

// 假如已经定义过
#ifdef 标识符

// 假如没有经定义过
#ifndef 标识符

// if结束指令
#endif

这等同于下面的 #if 命令:

#if defined 标识符
// 和
#if !defined 标识符

#endif

// 使用:
#if defined( __unix__ ) && defined( __GNUC__ )
/* ... */
#endif

# 条件编译

#if 表达式1
  [ 组1]
[#elif 表达式2
  [ 组2]]
...
[#elif 表达式n
  [ 组n ]]
[#else
  [ 组n+1 ]]
#endif

通过条件判断引用文件:

#define SYSTEM_N 10.3 

// 使用
#if (SYSTEM_N <= 10.5)
   #include "system_10.h"
#elif (SYSTEM_N <= 15)
   #include "system_15.h"
#else
   ...
#endif

或者:

#define SYSTEM_N "system_15.h"
...

// 使用(这种方式更简洁)
#ifdef SYSTEM_N
#include SYSTEM_H
#endif

# 预定义宏

ANSI C 定义了许多宏(不可修改),可以直接拿来使用即可

宏 描述
DATE 当前日期,一个以 "MMM DD YYYY" 格式表示的字符常量。
TIME 当前时间,一个以 "HH:MM:SS" 格式表示的字符常量。
FILE 这会包含当前文件名,一个字符串常量。
LINE 这会包含当前行号,一个十进制常量。
STDC 当编译器以 ANSI 标准编译时,则定义为 1。

# 预处理器运算符

C 预处理器提供了下列的运算符来帮助您创建宏:

# 1、宏使用-代码块

例如:求和

#include <stdio.h>

// 求和
void plus(int a, int b)
{
	int total = a + b;
	printf("%d + %d = %d", a, b, a + b);
}

int main(void)
{
	  int num_a = 1, num_b = 2;
	  plus(num_a, num_b); 
	  return 0;
}

运行结果: 1 +2 = 3

# 宏延续运算符(\)

多行内容换行使用:一个宏通常写在一个单行上。但是如果宏太长,一个单行容纳不下,则使用宏延续运算符(\)。

现在,我准备将上面plus代码直接使用宏来完成

#include <stdio.h>

// 求和
#define  plus(a, b)  \
    int total = a + b; \
	printf("%d + %d = %d", a, b, a + b);

int main(void)
{
	  int num_a = 1, num_b = 2;
	  plus(num_a, num_b);
	  return 0;
}

运行结果: 1 +2 = 3

结果和直接使用函数一样。

# 2、字符串常量化运算符(#)

将上述宏改如下:

#include <stdio.h>

// 加了#号,不管a,b传入的变量是否声明,都只获取变量名称的字符串
#define  plus(a, b)  \
    printf(#a " and " #b)

int main(void)
{
	  int a = 1;
	  plus(num_a, num_b); // num_b 未声明
	  return 0;
}

运行结果: num_a and num_b

不是打印变量值,而是变量名称,甚至未定义的变量都可以。

# 3、标记粘贴运算符(##)

宏定义内的标记粘贴运算符(##)会合并两个参数。它允许在宏定义中两个独立的标记被合并为一个标记。例如:

# 实例

#include <stdio.h>
 
#define plus(a, b) printf (#a #b " = %d", a##b)
 
int main(void)
{
   int ab = 88;
   plus(a, b);
   return 0;
}

结果:

ab = 88

其中:

#a :表示第一个参数变量名称
#b :表示第二个参数变量名称
a##b :表示a和b拼接成 ab 的值 88

# 参数化的宏

CPP 一个强大的功能是可以使用参数化的宏来模拟函数。例如,下面的代码是计算一个数的平方:

int square(int x) {
   return x * x;
}

我们可以使用宏重写上面的代码,如下:

#define square(x) ((x) * (x))

在使用带有参数的宏之前,必须使用 #define 指令定义。参数列表是括在圆括号内,且必须紧跟在宏名称的后边。宏名称和左圆括号之间不允许有空格。例如:

# 实例

#include <stdio.h>
 
#define MAX(x,y) ((x) > (y) ? (x) : (y))
 
int main(void)
{
   printf("最大值是 %d\n", MAX(80, 20));  
   return 0;
}

结果:

最大值是 80

# 别名

别名可以给类型取一个缩写或者自己更好记忆的名称,比如:

# 1、未使用别名的结构体:

#include <stdio.h>
#include <string.h>
 
struct animals 
{
  char  name[50];
  int   age;
};

int main( )
{
  struct animals bird; 
  strcpy(bird.name, "小鸟");
  bird.age = 2;

  printf( "bird name : %s\n", bird.name);
  printf( "bird age : %d\n", bird.age);
  return 0;
}

结果:

bird name : 小鸟
bird age : 2

# 2、使用别名的结构体:

#include <stdio.h>
#include <string.h>
 
typedef struct animals 
{
  char  name[50];
  int   age;
} People;

int main( )
{
  // 这个写法不需要写 struct,妥妥的的面向对象的概念了
  People peo;    
  
  strcpy(peo.name, "小明");
  peo.age = 15;

  printf( "peo name : %s\n", peo.name);
  printf( "peo age : %d\n", peo.age);

  return 0;
}

结果:

peo name : 小明
peo age : 15

其他例子比如 字节:

typedef unsigned char BYTE;

typedef 仅限于为类型定义符号名称

#define 不仅可以为类型定义别名,也能为数值定义别名,比如您可以定义 1 为 ONE。

typedef 是由编译器执行解释的,#define 语句是由预编译器进行处理的。

C语法Day4-指针
C语法Day6-文件

← C语法Day4-指针 C语法Day6-文件→

Theme by Vdoing
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式