C的预处理指令、typedef

1 集成开发环境

  keil-MDK是集成开发环境,集成了C编译器、宏汇编、链接器、库管理和仿真调试器等在内的完整开发方案;

  1.1 预处理器:处理注释和预处理指令;生成.i文件;

  1.2 编译器:对预处理器处理之后的文件进行语法分析编译,生成.s文件;

  1.3 汇编器:对编译器处理之后的文件进行翻译重定位,生成.o二进制文件;

  1.4 链接器:对汇编器处理之后的文件进行捆绑打包,生成.exe可执行文件;

1 预处理器指令

  作用:宏名替换,文件包含;

  特点:预处理器不会处理逻辑问题;预处理指令不是c指令,所以没有分号结尾;

  1.1 /**/ 注释

    预处理器会将每条注释用一个空格替代;

int/*想不到我是一个空格吧?*/num;
int num; //我也是呢;

  1.2 #define 宏

    作用:在函数中对宏名进行替换;

#define TWO       2
#define FOUR       TWO*TWO     
/*#define  宏       替换对象
*替换之后FOUR不是4,而是2*2;*/

    1.2.1 类函数宏

#define Byte0(data)          ((byte *)(&(data)))[0]        /*宏名为Byte0,参数为data*/
#define Byte1(data)          ((byte *)(&(data)))[1] 
#define Byte2(data)          ((byte *)(&(data)))[2]          
#define Byte3(data)          ((byte *)(&(data)))[3]
/* 相当于addr[0],addr[1],addr[2],addr[3],将原先存储着int型的地址,强制转换为byte型来存储,所以要声明4个指向byte型的指针;  */
/* 目的大概是为了防止数据丢失,然后在不同位数的单片机上移植;或者是为了特定目的传输数据;*/

    1.2.2 类函数宏

/*预处理器不会处理逻辑计算*/
#define SysTick_CLKSource_HCLK_Div8        ((uint32_t)0xFFFFFFFB)
#define SysTick_CLKSource_HCLK             ((uint32_t)0x00000004)
#define IS_SYSTICK_CLK_SOURCE(SOURCE)      (((SOURCE) == SysTick_CLKSource_HCLK) || \
                                           ((SOURCE) == SysTick_CLKSource_HCLK_Div8))

   1.3 #include文件包含

    作用:告诉预处理器将被包含文件的所有内容输入到当前位置;

#include <stdio.h>
/*告诉预处理器在标准目录(也就是库目录)下查找该文件;*/
#include "stdio.h"
/*告诉预处理器先在当前.c文件所在目录下查找该文件,找不到再去库中查找;*/

/*程序大小是由编译器编译时生成的代码决定的,与是否包含大型头文件无关;*/

   1.4 #undef

    作用:取消已有的预处理器指令;感觉用不上;

#define  SIZE        256
/*定义了一个宏SIZE,预处理器在文件中将其替换为256;*/
#undef  SIZE
/*取消了宏SIZE的定义,预处理器在文件中不会将其替换了;*/

  1.5 #ifdef

    作用:告诉编译器执行或忽略某些代码块;

    特点:只判断宏是否被声明,不判断宏是否为真;

#define USE_STD_LIB    1

#ifdef USE_STD_LIB             
int spi_std_init(void){
  return 0;
}
#else                        
int api_reg_init(void){
  return 0;
} 
#endif                       

  1.6 #ifndef

    作用:防止多次包含同一个文件,相同的宏被重新定义;

/*假设有math.h文件如下:*/
#ifndef SIZE
#define SIZE 512
#endif

/*当前文件为file1.h*/
#define SIZE 100
#include "math.h" 
/*由于前面一行定义过了SIZE,所以在math.h中就不会再重新替换SIZE为100;*/
/*那如果我把SIZE 10 放到math.h后面,SIZE的值是会改变,还是会报错呢??目测报错;*/

    1.6.1常见用法

#ifndef DRIVE_H_
#define DRIVE_H_

/*...假装好多预处理指令*/
    
#endif

/***标准函数库使用下划线作为前缀,自定义库可以去掉_前缀作为区分;
***就约定自定义库用大写字母加下划线定义好了;
***预处理指令可以避免头文件的重复包含;***/
#define    USE_STD_LIB      1

/***条件编译语句,大概预处理器只是进行了替换,实际判断还是编译器处理的;***/
#if USE_STD_LIB
int gpio_std_init(void){
    return 0;
}

#elif USE_HAL_LIB
int gpio_hal_init(void){
    return 0;
}

#else
int gpio_reg_init(void){
    return 0;
}
#endif
/***#ifndef可以防止预处理器多次处理相同的xx.h文件,不能防止xx.h中定义式声明的多次重复;
***大约预处理器只是对用户代码中的#include进行了文件替换包含,而实际编译处理还是编译器编译的;
***在xx.h中进行定义式声明,则编译器会多次声明定义造成报错;
***大约编译器只会编译一次xx.c,而xx.h中的函数声明只是一个函数接口链接;
***解决办法是要么使用static副本,要么定义式声明在xx.c文件内,要么使用extern引用式声明;***/

    1.6.2 static副本

      作用:解决xx.h头文件中的定义式重复声明

/*以下代码为xx.h*/

static const double PI = 3.1415 ;
static const months = 12 ;
/*如果去掉static,将导致每个包含xx.h的C文件都有一个相同的定义式声明,会让cpu不知所措;
*定义式声明只能声明一次,引用式声明可以多次;*/


void sum(int a,int b);
/*将函数原型,全局变量等放在头文件中,然后在其他多个文件中包含该头文件,
*就相当于给每个文件都提供了一个接口沟通 
*如果头文件中含有定义式声明,那么前面需要加上一个static,这相当于给每个调用的文件一个单独的数据副本;*/

    1.6.3 extern引用式声明

      作用:解决xx.h头文件中的定义式重复声明

/**xx.c文件中**/
int receive_buf[256];

/***xx.c对应的xx.h文件中引用式声明,这样该数组就可以提供给其他xx.h文件使用了;***/
extern int receive_buf[256];

2 编译器指令

  2.1 typedef 声明

typedef unsigned char      BYTE ;             /* 将 unsigned char 重新命名为BYTE ;*/
typedef char *             STRING ;           /*将 char * 声明为STRING */

  2.2 typedef 与 #define

typedef  char *        STRING;
#define  STRING        char * 


/*1,typedef只能定义数据类型,而#define可以定义类对象宏、类函数宏;*/
/*2,typedef由编译器解释,而#define由预处理器解释;*/
/*3,如果定义在函数外则具有文件作用域,函数内则局部作用域*/

  2.3 typedef 与 结构体

    2.3.1 重新声明结构体

/* 声明结构体类型complex;将其重新声明为COMPLEX; */
typedef struct complex{
int real;
float imag;
} COMPLEX;

/*声明一个没有名字的结构体类型,将其声明为COMPLEX类型;
*typedef声明时,结构体类型可以省略原标识符,只保留struct标识符;*/
typedef struct{
int real;
float imag;
} COMPLEX;

    2.3.2 typedef 与 指针

struct rt_thread
{
};
typedef struct rt_thread  *rt_thread_t;
//此时 rt_thread_t 是一个 rt_thread 类型的指针;
//为什么我的关注点是放在 这个指针的符号*为什么加在rt_thread_t上,而不是单独写;这又是什么固定格式把。。。

  2.4 #pragma

    作用:可以放入代码中的编译器指令;可是用了预处理指令的#,迷惑,先放着吧;

  2.5 sizeof()

    这不是函数也不是编译器指令,而是C语言运算符,运算符优先级2级,由编译器编译执行;

posted @ 2020-02-28 23:09  caesura_k  阅读(378)  评论(0编辑  收藏  举报