//extern int ADD(int x, int y);//声明引用外部文件
 //c语言预处理
 // 文本文件  翻译+链接         二进制文件   运行 
 //test.cpp————————》test.exe————————》
 // 编译器   翻译环境  链接器             执行环境
 //      test.obj(目标文件)
 // 
 //(linux系统)翻译器:
 // gcc -E
 // 预编译(test.i):把头文件放进文件里 
 //                   删除注释(空格替换)
 //                   #define 定义的进行替换
 // gcc -S
 // 编译(test.s):把c代码翻译成汇编代码
 //                 (语法分析,词法分析,语义分析,符号汇总)
 // gcc -c
 //汇编(test.o):形成符号表
 //               把汇编代码转换成二进制代码指令
 //               obj文件(目标文件) 二进制文件
 // 
 // 链接器:
 //  多个 .obj 文件通过链接器链接
 //  合并段表(符号表):(固定格式分段) elf格式  多个程序分段形成符号表 
 //  符号表的合并和重定位: 多个符号表合并,
 //                         符号重定位(把一个文件内的声明的符号地址改成
 //                          另一个文件的实际有效地址),这样就链接起来了
 // 
 // 
 // 运行环境:
 //         程序必须载入内存中:有操作系统就由操作系统完成
 //                         独立环境中,程序需要手动安排,或可执行代码置入只读内存
 //         程序开始执行,调用main函数
 //         开始执行代码,使用一个运行时堆栈(stack),存储函数局部变量和返回地址,
 //             同时可以使用静态(static)内存中的变量并在执行过程中一直保留他们的值
 //         终止程序,正常终止,或意外终止
 //
 // 预处理详解:
 //     预定义符号: __FILE__ 进行编译的源文件——当前文件的路径
 //                  __LINE__ 文件当前的行号——当前代码的行号
 //                  __DATE__ 文件被编译的日期
 //                  __TIME__ 文件被编译的实际
 //                  __STDC__ 如果编译器遵循ANSI C 值为1, 否则未定义
 //                  ...
 //     预处理指令:
 //                #define// 定义的静态值,可在程序中完成替换,不能在内容后面加;
 //                #include
 //                #pragma pack(4) 设置默认对齐数
 //                #pragma pack()
 //                #if
 //                #endif
 //                #error
 //                #line
 //                #开头定义的指令
 //
  
//#define 定义标示符号 #define name stuff  name名字  stuff内容
 //#define MAX 100
 //#define STR "HEHE"
 //#define reg register//存储器关键字
 //#define do_forever for(::)//用形象的符号替换一种函数实现
 //#define CASE break:case//在case 语句后面自动把break写上
 //#define DEBUG_PRINT printf("%s\n", __TIME__)//如果内容过长,每行加上 \ (续航符)
 //#define定义宏
 //#define机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏(macro)
 //           定义宏(define macro)
 //           #define name(parament-list) stuff    
 //           parament-list是参数列表,用逗号隔开
 //           参数可以出现在suff里面注:左括号和name紧贴一起
 //           stuff内容根据符号优先级用()进行分装
 //
 // #define 替换规则
 // 1;在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号;
 //   发现有就进行替换。
 // 2,替换文本随后被插入程序中的原文本位置,对于宏,参数名都会被他们的值替换
 // 3,最后,再次扫描是否有#define定义的符号,发现有就再次替换
 //    注意:
 //        1:宏参数和#define可以出去其他#define定义的符号,但是宏不能递归
 //        2:预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索
 //
 // 
 // #和##
 //    # 字符串常量化运算符  把宏参数插入到字符串中    
 //    ## 合并粘贴运算符,可以把它两边的符号合成一个符号,
 //       允许宏定义从分离的文本片段创建标识符
 //
 // 
 // 带有副作用的宏参数 
 // 
 // 函数有参数类型,宏没有参数类型
 // 
 // #undef 取消宏定义
 // 
 // 
 // 命令行定义
 //  在程序执行时,许多编译器提供了允许在命令行中定义符号的功能(给程序中的变量赋值)
 // 有的命令行有参数 有的没有
 // 
 // 
 // 条件编译指令
 //     选择性的对语句进行编译或者放弃,有条件的编译指令
 //    1, #if 常量表达式
 //       #endif   ——结束
 // 
 //    2,#if 常量表达式   ---如果
 //       #elif 常量表达式 ——或如果
 //       #else 常量表达式 ——否则
 //       #endif           ——结束
 // 
 //    3,判断是否被定义
 //       #if defined(symbol)--如果定义了symbol
 //       #ifdef symbol 和上一句等价   
 //       
 //       #if !defined(symbol) --如果没有定义symbol
 //       #ifndef symbol 和上一句等价
 //       
 //       #endif   ---结束
 //    4,嵌套指令
 //       #if defined(str1)//如果定义了str1
 //             #ifdef ptr1//如果定义了ptr1
 //                 r1();//执行r1
 //             #endif  ---内结束
 //             #ifdef ptr2//如果定义了ptr2
 //                 r2();执行r2
 //             #endif  ---内结束
 //       #elif defined(str2)//或如果定义了str2
 //             #ifdef ptr2//如果定义了ptr2
 //                  r2();//执行r2
 //             #endif  ---内结束
 //       #endif  ---外结束
 // 
 //
 // 文件包含 #include
 //      使另一个文件被编译,拷贝到当前文件里
 //       <库函数头文件> 在标准路径(安装路径)去查找
 //     "自编(本地)头文件"  
 //    查找策略:先查源文件所在目录下找,未发现就在标准位置(安装路径)查找,最后未发现提示编译错误
 //   
 // 
 //    嵌套文件重复包含头文件解决方案
 // 方案1, #ifndef __TEST_H__
 //        #define __TEST_H__
 //        (头文件内容)
 //        #endif
 // 方案2,#pragma once 
 //    
//含参数的宏定义
 //#define MAX 100
 //#define SQUARE(X) ((X)*(X))//stuff内容根据符号优先级用()进行分装
//#把宏定义参数插入字符串里
 //void print(int x)
 //{
 //    printf("the value of x is %d\n", x);//x是固定的,不能转换a或者b
 //}
 //#define PRINT(X) printf("the value of "#X" is %d\n", X)//多个字符串放在一起会自动连在一起
//##字符串粘连
 //#define CAT(X,Y) X##Y//把X的内容和Y的内容连在一起
//宏定义参数中存在++ -- 等副作用参数
 //#define MAX(X,Y) ((X)>(Y)?(X):(Y))  //给X,Y 的变量是直接整体替换的,不会进行计算
 //#define 优势:预处理完成替换直接到判断语句,没有函数调用和返回的开销,更省时间,
 //        宏没有参数类型(通用),还可以传(类型)参数
 //        劣势:宏会直接将代码插入到程序中,如果宏很长,就会增加程序的长度
 //              宏不能调试,没参数类型,不严谨
 //              宏会有运算符优先级带来的副作用
 //函数会有调用和返回的开销,且有函数参数类型要求
//#undef MAX 取消宏定义
//int Max(int x, int y)
 //{
 //    return (x > y ? x : y);
 //}
//#define DEBUG
 int main()
 {
    int arr[10] = { 0 };
     int i = 0;
     int a = 10;
     int b = 20;
     FILE* pf = fopen("log.txt", "w");
     for (i = 0; i < 10; i++)
     {
         arr[i] = i;
         fprintf(pf, "file:%s line:%d date:%s time:%s i = %d\n",
             __FILE__,__LINE__,__DATE__,__TIME__,i);
     }
     fclose(pf);
     pf = NULL;
     for (i = 0; i < 10; i++)
     {
         printf("%d ", arr[i]);
     }
     int c = ADD(a, b);
     printf("%d", c);
     //printf("%s\n", __FILE__);//当前文件的路径
     //printf("%d\n", __LINE__);//当前代码的行号
     //printf("%s\n", __DATE__);
     //printf("%s\n", __TIME__);
     //printf("%d\n", __STDC__);VS是未定义这个的
    //宏定义参数替换
     int ret = SQUARE(5);//ret = 25;
     ret = SQUARE(5 + 1);//ret = 11;宏是直接用5+1替换X,使用得到11,
     //可以在宏定义用(X)进行分开成单独个体
     ret = SQUARE(MAX);
     printf("MAX= %d\n", MAX);//"MAX= %d\n"中的字符串MAX不会被#define检测替换
    //#把宏参数插入到字符串中
     int a = 10;
     int b = 20;
     print(a);
     print(b);
     PRINT(a);
     printf("the value of "#X" is %d\n", X)
     PRINT(b);
    //##把字符串粘连在一起
     //int abcd10 = 2019;
     //printf("%d\n", CAT(abcd, 10));//abcd##10 == abcd10
    //带有副作用的宏参数
     int a = 10;
     int b = 11;
     int max = MAX(a++, b++);//++改变了a和b的值
     max = ((a++)>(b++)?(a++):(b++))
     // 这个代码执行:先比较a和b b大,然后a++,b++,再把b的赋值给max,最后b++
     printf("%d\n", max);//12
     printf("%d\n", a);//11
     printf("%d\n", b);//13
    //函数和宏比较
     int a = 10;
     int b = 11;
     float c = 3.0f;
     float d = 3.1f;
     int max = MAX(a, b);
     printf("%d\n", max);
     max = Max(a, b);
     printf("%d\n", max);
     float max1 = MAX(c, d);
    //条件编译
     int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
     int i = 0;
     for (i = 0; i < 10; i++)
     {
         arr[i] = 0;
 #ifdef DEBUG//如果DEBUG定义了,就执行printf
 #if defined DEBUG//如果DEBUG定义了,就执行printf
 #ifndef DEBUG//如果没有定义DEBUG
 #if !defined DEBUG
         printf("%d ", arr[i]);
 #endif // DEBUG  结束条件编译
        
     }
#if 1==0
     printf("1\n");
 #elif 2==2
     printf("2\n");
 #else
     printf("3")
#endif
    return 0;
 }